ref: move StringPool cache into StringPool

This commit is contained in:
relikd
2025-11-27 20:54:15 +01:00
parent 583faa4f9c
commit fd522d612f
2 changed files with 20 additions and 11 deletions

View File

@@ -53,6 +53,7 @@ public struct StringPool {
let dataString: Index
let dataStyle: Index
}
private var _lookupTable: [StringPoolRef: String] = [:]
init(_ idx: Index, _ bytes: RawBytes) throws {
self.init(try ChunkHeader(idx, bytes, expect: .StringPool))
@@ -81,6 +82,9 @@ public struct StringPool {
/// Retrieve string for given `StringPoolRef` index
public func getString(_ idx: StringPoolRef) -> String {
precondition(idx < stringCount, "Index out of bounds for string-pool string[\(idx) > \(stringCount)]")
if let cached = _lookupTable[idx] {
return cached
}
let offset = header._bytes.int32(_offset.indicesString + Index(idx) * 4)
let dataOffset = _offset.dataString + Index(offset)
return flags.contains(.Utf8)
@@ -95,4 +99,15 @@ public struct StringPool {
let dataOffset = _offset.dataStyle + Index(offset)
return StringPoolSpan(dataOffset, header._bytes)
}
/// Performs `getString()` and caches the results in a lookup-table.
/// For memory-efficiency you should use this only on keys which are expected to repeat.
public mutating func getStringCached(_ index: StringPoolRef) -> String {
if let cached = _lookupTable[index] {
return cached
}
let val = self.getString(index)
_lookupTable[index] = val
return val
}
}

View File

@@ -95,15 +95,9 @@ public struct Xml_Parser {
/// Iterate over whole tree and call `start` and `end` blocks for each element.
/// Each `start` block contains the `tag` name and its attribute `(name, value)` pairs.
public func iterElements(_ start: (_ startTag: String, _ attributes: AttributesDict) throws -> Void, _ end: (_ endTag: String) throws -> Void) throws {
// cache StringPool lookups for faster access
var _lookupTable: [StringPoolRef: String] = [:]
var pool = stringPool
func lookup(_ index: StringPoolRef) -> String {
if let cached = _lookupTable[index] {
return cached
}
let val = stringPool.getString(index)
_lookupTable[index] = val
return val
pool.getStringCached(index)
}
// cache namespace prefix strings
var namespaces: [StringPoolRef: String] = [:]
@@ -120,7 +114,7 @@ public struct Xml_Parser {
case .XmlStartNamespace:
let ns = XmlNamespace(chunk)
namespaces[ns.uri] = lookup(ns.prefix)
namespaces[ns.uri] = pool.getStringCached(ns.prefix)
nsOnNextTag = ns.uri
case .XmlEndNamespace:
@@ -131,10 +125,10 @@ public struct Xml_Parser {
let elem = XmlStartElement(chunk)
let attrs = AttributesDict(elem, stringPool, lookupFn: lookup(_:), namespaces: namespaces, nsNeedsAppend: nsOnNextTag)
nsOnNextTag = nil
try start(lookup(elem.name), attrs)
try start(pool.getStringCached(elem.name), attrs)
case .XmlEndElement:
try end(lookup(XmlEndElement(chunk).name))
try end(pool.getStringCached(XmlEndElement(chunk).name))
default:
throw AXMLError("Dont know how to handle \(chunk.type) (offset: \(chunk.index(.startOfChunk)))")