Files
AndroidXML/Sources/AndroidXML/ChunkHeader.swift
2025-11-25 22:46:14 +01:00

82 lines
2.6 KiB
Swift

/**
* Header that appears at the front of every data chunk in a resource.
*/
/// Header size: `8 Bytes`
struct ChunkHeader {
// INTERNAL USAGE. No correspondence in data:
private let _offset: Index
internal let _bytes: RawBytes
/// Type identifier for this chunk. The meaning of this value depends on the containing chunk.
let type: ChunkType // UInt16
/// Size of the chunk header (in bytes).
/// Adding this value to the address of the chunk allows you to find its associated data (if any).
let headerSize: UInt16
/// Total size of this chunk (in bytes).
/// This is the chunkSize plus the size of any data associated with the chunk.
/// Adding this value to the chunk allows you to completely skip its contents (including any child chunks).
/// If this value is the same as chunkSize, there is no data associated with the chunk.
let chunkSize: UInt32
init(_ idx: Index, _ bytes: RawBytes, expect: ChunkType? = nil) throws {
let rawType = bytes.int16(idx)
guard let typ = ChunkType(rawValue: rawType) else {
throw AXMLError("Unexpected chunk type \(rawType)")
}
if let expect, expect != typ {
throw AXMLError("Expected chunk type \(expect) but got \(typ)")
}
_offset = idx
_bytes = bytes
type = typ
headerSize = bytes.int16(idx + 2)
chunkSize = bytes.int32(idx + 4)
}
/// Go over all child elements by iterating over them one-by-one
func iterChildren(filter: ((ChunkHeader) -> Bool)? = nil, _ block: (_ abort: inout Bool, _ chunk: ChunkHeader) throws -> Void) throws {
var nextIdx = index(.startOfData)
let lastIdx = index(.afterChunk)
while nextIdx < lastIdx {
let child = try ChunkHeader(nextIdx, _bytes)
nextIdx = child.index(.afterChunk)
guard filter?(child) ?? true else {
continue
}
var abort = false
try block(&abort, child)
if abort {
break
}
}
}
/// Collect all children with `iterChildren` and return full list
func children(filter: ((ChunkHeader) -> Bool)? = nil) throws -> [ChunkHeader] {
var result: [ChunkHeader] = []
try iterChildren(filter: filter) { result.append($1) }
return result
}
/// Convert relative offset to absolute offset
func index(_ relative: RelativeIndex) -> Index {
switch relative {
case .startOfChunk: _offset
case .startOfHeader: _offset + 8 // chunk header length
case .startOfData: _offset + Index(headerSize)
case .afterChunk: _offset + Index(chunkSize)
}
}
enum RelativeIndex {
case startOfChunk
case startOfHeader
case startOfData
case afterChunk
}
func byteReader(at offset: RelativeIndex, skip: Index = 0) -> RawBytes.Reader {
.init(data: _bytes, index: index(offset) + skip)
}
}