Skip to content

Instantly share code, notes, and snippets.

@neikeq
Last active August 29, 2015 14:13
Show Gist options
  • Save neikeq/211f7826d3589a641c0b to your computer and use it in GitHub Desktop.
Save neikeq/211f7826d3589a641c0b to your computer and use it in GitHub Desktop.
Simple byte buffer sequential reader/writer.
# The MIT License (MIT)
#
# Copyright (c) 2015 Ignacio Roldán Etcheverry
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
class BinaryReader:
"""
A sequential byte buffer reader.
"""
def __init__(self, data, byteorder='big'):
"""
Keyword arguments:
data -- the byte array to read from
byteorder -- endianness used to read from the array. (default 'big')
"""
self.data = data
self.byteorder = byteorder
self.index = 0
def set_index(self, index):
"""
Set the index of this buffer.
"""
self.index = index
def readable_bytes(self):
"""
Returns the number of readable bytes.
The result equals to (len(self.data) - self.index).
"""
return len(self.data) - self.index
def read_bool(self, length):
"""
Returns True if the value at the current index is not equal to zero,
otherwise returns False. Increases index by length.
Keyword arguments:
length -- the number of bytes to read
"""
data = self.data[self.index:self.index + length]
self.index += length
return int.from_bytes(data, byteorder=self.byteorder) != 0
def read_byte(self):
"""
Returns the byte at the current index and increases index by 1.
"""
value = self.data[self.index]
self.index += 1
return value
def read_short(self):
"""
Returns the 16-bit int at the current index and increases index by 2.
"""
data = self.data[self.index:self.index + 2]
self.index += 2
return int.from_bytes(data, byteorder=self.byteorder)
def read_int(self):
"""
Returns the 32-bit int at the current index and increases index by 4.
"""
data = self.data[self.index:self.index + 4]
self.index += 4
return int.from_bytes(data, byteorder=self.byteorder)
def read_string(self, length, encoding='utf-8'):
"""
Returns the decoded string at the current index
and increases index by length.
Keyword arguments:
length -- the number of bytes to decode
encoding -- the encoding used to decode the bytes (default 'utf-8')
"""
value = self.data[self.index:self.index + length].decode(encoding)
self.index += length
return value
def read_bytes(self, length):
"""
Returns the byte array at the current index and
increases index by length.
Keyword arguments:
length -- the number of bytes to read
"""
value = bytes(self.data[self.index:self.index + length])
self.index += length
return value
def ignore_bytes(self, number):
"""
Increases the index by the specified number of bytes to ignore.
"""
self.index += number
# The MIT License (MIT)
#
# Copyright (c) 2015 Ignacio Roldán Etcheverry
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
class BinaryWriter:
"""
A sequential byte buffer writer.
"""
def __init__(self, byteorder='big'):
"""
Keyword arguments:
byteorder -- endianness used to read from the array. (default 'big')
"""
self.data = bytearray()
self.byteorder = byteorder
def array(self):
"""
Return the byte array of this buffer.
"""
return self.data
def size(self):
"""
Returns the size of the array.
"""
return len(self.data)
def write_bool(self, value, length=1):
"""
Appends the specified 8-bit bool.
Keyword arguments:
value -- the bool to write
length -- the number of bytes to write
"""
self.data.extend(value.to_bytes(length, self.byteorder))
def write_byte(self, value):
"""
Appends the specified 8-bit byte.
Keyword arguments:
value -- the byte to write
"""
self.data.extend(value.to_bytes(1, self.byteorder))
def write_short(self, value):
"""
Appends the specified 16-bit integer.
Keyword arguments:
value -- the int to write
"""
self.data.extend(value.to_bytes(2, self.byteorder))
def write_int(self, value):
"""
Appends the specified 32-bit integer.
Keyword arguments:
value -- the int to write
"""
self.data.extend(value.to_bytes(4, self.byteorder))
def write_string(self, value, encoding='utf-8'):
"""
Appends the specified string represented by the specified encoding.
Keyword arguments:
value -- the string to write.
encoding -- the encoding used to write de string (default 'utf-8')
Returns:
The number of bytes written
"""
data = bytes(value, encoding)
self.data.extend(data)
return len(data)
def write_string_fixed(self, value, length, encoding='utf-8'):
"""
Appends the specified string represented by the specified encoding.
The string is limited by the length specified. If the string is
shorter than the limit, the remain space is filled with NUL zeros.
Keyword arguments:
value -- the string to write.
length -- the length of bytes to write
encoding -- the encoding used to write de string (default 'utf-8')
"""
data = bytes(value, encoding)
self.data.extend(data[0:length])
if len(data) < length:
self.write_zeros(length - len(data))
def write_bytes(self, value):
"""
Appends the specified byte array.
Keyword arguments:
value -- the byte array to write
"""
self.data.extend(value)
def write_zeros(self, number):
"""
Fill the buffer with NUL zeros from the current index
and increases index by the specified number.
"""
self.data.extend(bytes(number))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment