From 2f1bed55439b937060db2b2aaa964037a54017d9 Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Thu, 21 Nov 2024 08:38:52 +0900 Subject: [PATCH] WIP --- Sources/WasmParser/LEB.swift | 77 +++++++++++++++++++++++++++ Sources/WasmParser/MemoryBuffer.swift | 27 ++++++++++ 2 files changed, 104 insertions(+) create mode 100644 Sources/WasmParser/MemoryBuffer.swift diff --git a/Sources/WasmParser/LEB.swift b/Sources/WasmParser/LEB.swift index df99feec..042c7e0b 100644 --- a/Sources/WasmParser/LEB.swift +++ b/Sources/WasmParser/LEB.swift @@ -81,3 +81,80 @@ func decodeLEB128( return IntType(bitPattern: result) } + +@inlinable +func decodeLEB128( + stream: inout BufferSlice.Cursor +) throws -> IntType where IntType: FixedWidthInteger, IntType: UnsignedInteger { + let firstByte = try stream.next() + var result: IntType = IntType(firstByte & 0b0111_1111) + if _fastPath(firstByte & 0b1000_0000 == 0) { + return result + } + + var shift: UInt = 7 + + while true { + let byte = try stream.next() + let slice = IntType(byte & 0b0111_1111) + let nextShift = shift + 7 + if nextShift >= IntType.bitWidth, (byte >> (UInt(IntType.bitWidth) - shift)) != 0 { + throw LEBError.integerRepresentationTooLong + } + result |= slice << shift + shift = nextShift + + guard byte & 0b1000_0000 != 0 else { break } + } + + return result +} + +@inlinable +func decodeLEB128( + stream: inout BufferSlice.Cursor +) throws -> IntType where IntType: FixedWidthInteger, IntType: RawSignedInteger { + let firstByte = try stream.next() + var result = IntType.Unsigned(firstByte & 0b0111_1111) + if _fastPath(firstByte & 0b1000_0000 == 0) { + // Interpret Int${Self.bitWidth-1} as Int${Self.bitWidth} + return (IntType(bitPattern: result) << (IntType.bitWidth - 7)) >> (IntType.bitWidth - 7) + } + + var shift: IntType = 7 + + var byte: UInt8 + repeat { + byte = try stream.next() + + let slice = IntType.Unsigned(byte & 0b0111_1111) + result |= slice << shift + + // When we don't have enough bit width + if shift > (IntType.bitWidth - 7) { + let remainingBitWidth = IntType.bitWidth - Int(shift) + let continuationBit = (byte & 0b1000_0000) != 0 + // When a next byte is expected + if continuationBit { + throw LEBError.integerRepresentationTooLong + } + + let signAndDiscardingBits = Int8(bitPattern: byte << 1) >> remainingBitWidth + // When meaningful bits are discarded + if signAndDiscardingBits != 0 && signAndDiscardingBits != -1 { + throw LEBError.overflow + } + return IntType(bitPattern: result) + } + + shift += 7 + } while byte & 0b1000_0000 != 0 + + // Sign flag is second high-order bit + if byte & 0b0100_0000 != 0 { + // Sign extend + result |= IntType.Unsigned(bitPattern: ~0) << shift + } + + return IntType(bitPattern: result) +} diff --git a/Sources/WasmParser/MemoryBuffer.swift b/Sources/WasmParser/MemoryBuffer.swift new file mode 100644 index 00000000..e4d825c2 --- /dev/null +++ b/Sources/WasmParser/MemoryBuffer.swift @@ -0,0 +1,27 @@ +@usableFromInline +struct BufferSlice { + @usableFromInline + let bytes: [UInt8] + @usableFromInline + let sourceOffset: Int + + /// Similar to `IndexingIterator` but throws an error when `next()` + /// is called at the end of the buffer + @usableFromInline + struct Cursor { + @usableFromInline + let _slice: BufferSlice + @usableFromInline + var _offset: Int + + @inlinable + mutating func next() throws -> UInt8 { + guard _offset < _slice.bytes.count else { + throw WasmParserError(.unexpectedEnd, offset: _slice.sourceOffset + _offset) + } + let consumed = _slice.bytes[_offset] + _offset += 1 + return consumed + } + } +}