| # Licensed to the Apache Software Foundation (ASF) under one | |
| # or more contributor license agreements. See the NOTICE file | |
| # distributed with this work for additional information | |
| # regarding copyright ownership. The ASF licenses this file | |
| # to you under the Apache License, Version 2.0 (the | |
| # "License"); you may not use this file except in compliance | |
| # with the License. You may obtain a copy of the License at | |
| # | |
| # http://www.apache.org/licenses/LICENSE-2.0 | |
| # | |
| # Unless required by applicable law or agreed to in writing, | |
| # software distributed under the License is distributed on an | |
| # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | |
| # KIND, either express or implied. See the License for the | |
| # specific language governing permissions and limitations | |
| # under the License. | |
| """Input for byte array.""" | |
| import struct | |
| class MemorySliceInput: | |
| """Input for byte array.""" | |
| def __init__(self, data: bytes): | |
| self.data = data | |
| self._position = 0 | |
| def position(self) -> int: | |
| """Get current position.""" | |
| return self._position | |
| def set_position(self, position: int) -> None: | |
| """Set current position.""" | |
| if position < 0 or position > len(self.data): | |
| raise IndexError(f"Position {position} out of bounds [0, {len(self.data)}]") | |
| self._position = position | |
| def is_readable(self) -> bool: | |
| """Check if there are more bytes to read.""" | |
| return self.available() > 0 | |
| def available(self) -> int: | |
| """Get number of available bytes.""" | |
| return len(self.data) - self._position | |
| def read_byte(self) -> int: | |
| """Read a single byte.""" | |
| if self._position >= len(self.data): | |
| raise IndexError("No more bytes available") | |
| value = self.data[self._position] | |
| self._position += 1 | |
| return value | |
| def read_unsigned_byte(self) -> int: | |
| """Read an unsigned byte.""" | |
| return self.read_byte() & 0xFF | |
| def read_int(self) -> int: | |
| """Read a 4-byte integer (big-endian).""" | |
| if self._position + 4 > len(self.data): | |
| raise IndexError("Not enough bytes to read int") | |
| value = struct.unpack('>I', self.data[self._position:self._position + 4])[0] | |
| self._position += 4 | |
| return value | |
| def read_var_len_int(self) -> int: | |
| """Read a variable-length integer.""" | |
| result = 0 | |
| offset = 0 | |
| while offset < 32: | |
| b = self.read_unsigned_byte() | |
| result |= (b & 0x7F) << offset | |
| if (b & 0x80) == 0: | |
| return result | |
| offset += 7 | |
| raise ValueError("Malformed integer") | |
| def read_long(self) -> int: | |
| """Read an 8-byte long (big-endian).""" | |
| if self._position + 8 > len(self.data): | |
| raise IndexError("Not enough bytes to read long") | |
| value = struct.unpack('>Q', self.data[self._position:self._position + 8])[0] | |
| self._position += 8 | |
| return value | |
| def read_var_len_long(self) -> int: | |
| """Read a variable-length long.""" | |
| result = 0 | |
| offset = 0 | |
| while offset < 64: | |
| b = self.read_unsigned_byte() | |
| result |= (b & 0x7F) << offset | |
| if (b & 0x80) == 0: | |
| return result | |
| offset += 7 | |
| raise ValueError("Malformed long") | |
| def read_slice(self, length: int) -> bytes: | |
| """Read a slice of bytes.""" | |
| if self._position + length > len(self.data): | |
| raise IndexError(f"Not enough bytes to read slice of length {length}") | |
| value = self.data[self._position:self._position + length] | |
| self._position += length | |
| return value |