Commits

Anonymous committed e231c02

hachoir-core: add MIDDLE_ENDIAN (BADC order, as used on the PDP-11 and in the LZX format)

  • Participants
  • Parent commits 49977f4

Comments (0)

Files changed (9)

hachoir-core/hachoir_core/bits.py

 string, number, hexadecimal, etc.
 """
 
-from hachoir_core.endian import BIG_ENDIAN, LITTLE_ENDIAN
+from hachoir_core.endian import BIG_ENDIAN, LITTLE_ENDIAN, MIDDLE_ENDIAN
 from hachoir_core.compatibility import reversed
 from itertools import chain, repeat
 from struct import calcsize, unpack, error as struct_error
          | ((value & 0x00FF0000L) >> 8) \
          | ((value & 0xFF000000L) >> 24)
 
+def arrswapmid(data):
+    r"""
+    Convert an array of characters from middle-endian to big-endian and vice-versa.
+
+    >>> arrswapmid("badcfehg")
+    ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']
+    """
+    assert len(data)%2 == 0
+    ret = ['']*len(data)
+    ret[1::2] = data[0::2]
+    ret[0::2] = data[1::2]
+    return ret
+
+def strswapmid(data):
+    r"""
+    Convert raw data from middle-endian to big-endian and vice-versa.
+
+    >>> strswapmid("badcfehg")
+    'abcdefgh'
+    """
+    return ''.join(arrswapmid(data))
+
 def bin2long(text, endian):
     """
     Convert binary number written in a string into an integer.
     assert endian in (LITTLE_ENDIAN, BIG_ENDIAN)
     bits = [ (ord(character)-ord("0")) \
         for character in text if character in "01" ]
-    assert len(bits) != 0
     if endian is not BIG_ENDIAN:
         bits = reversed(bits)
+    size = len(bits)
+    assert 0 < size
     value = 0
     for bit in bits:
         value *= 2
     '\x19\x12\x00\x00'
     """
     assert (not size and 0 < value) or (0 <= value)
-    assert endian in (LITTLE_ENDIAN, BIG_ENDIAN)
+    assert endian in (LITTLE_ENDIAN, BIG_ENDIAN, MIDDLE_ENDIAN)
     text = []
     while (value != 0 or text == ""):
         byte = value % 256
     else:
         need = 0
     if need:
-        if endian is BIG_ENDIAN:
+        if endian is LITTLE_ENDIAN:
+            text = chain(text, repeat("\0", need))
+        else:
             text = chain(repeat("\0", need), reversed(text))
-        else:
-            text = chain(text, repeat("\0", need))
     else:
-        if endian is BIG_ENDIAN:
+        if endian is not LITTLE_ENDIAN:
             text = reversed(text)
+    if endian is MIDDLE_ENDIAN:
+        text = arrswapmid(text)
     return "".join(text)
 
 def long2bin(size, value, endian, classic_mode=False):
     True
     >>> str2long("\xff\xff\xff\xff\xff\xff\xff\xff", BIG_ENDIAN) == (2**64-1)
     True
+    >>> str2long("\x0b\x0a\x0d\x0c", MIDDLE_ENDIAN) == 0x0a0b0c0d
+    True
     """
     assert 1 <= len(data) <= 32   # arbitrary limit: 256 bits
     try:
     except KeyError:
         pass
 
-    assert endian in (BIG_ENDIAN, LITTLE_ENDIAN)
+    assert endian in (BIG_ENDIAN, LITTLE_ENDIAN, MIDDLE_ENDIAN)
     shift = 0
     value = 0
     if endian is BIG_ENDIAN:
         data = reversed(data)
+    elif endian is MIDDLE_ENDIAN:
+        data = reversed(strswapmid(data))
     for character in data:
         byte = ord(character)
         value += (byte << shift)
         shift += 8
     return value
-

hachoir-core/hachoir_core/endian.py

 
 BIG_ENDIAN = "ABCD"
 LITTLE_ENDIAN = "DCBA"
+MIDDLE_ENDIAN = "BADC"
 NETWORK_ENDIAN = BIG_ENDIAN
 
 endian_name = {
     BIG_ENDIAN: _("Big endian"),
     LITTLE_ENDIAN: _("Little endian"),
+    MIDDLE_ENDIAN: _("Middle endian"),
 }
-

hachoir-core/hachoir_core/field/basic_field_set.py

 from hachoir_core.field import Field, FieldError
 from hachoir_core.stream import InputStream
-from hachoir_core.endian import BIG_ENDIAN, LITTLE_ENDIAN
+from hachoir_core.endian import BIG_ENDIAN, LITTLE_ENDIAN, MIDDLE_ENDIAN
 from hachoir_core.event_handler import EventHandler
 
 class ParserError(FieldError):
             self._global_event_handler = None
 
         # Sanity checks (post-conditions)
-        assert self.endian in (BIG_ENDIAN, LITTLE_ENDIAN)
+        assert self.endian in (BIG_ENDIAN, LITTLE_ENDIAN, MIDDLE_ENDIAN)
         if (self._size is not None) and (self._size <= 0):
             raise ParserError("Invalid parser '%s' size: %s" % (self.path, self._size))
 

hachoir-core/hachoir_core/field/character.py

 
     def createDisplay(self):
         return makePrintable(self.value, "ASCII", quote="'", to_unicode=True)
-

hachoir-core/hachoir_core/field/enum.py

     field.createDisplay = createDisplay
     field.getEnum = lambda: enum
     return field
-

hachoir-core/hachoir_core/field/generic_field_set.py

     document).
 
     Class attributes:
-    - endian: Bytes order (L{BIG_ENDIAN} or L{LITTLE_ENDIAN}). Optional if the
-      field set has a parent ;
+    - endian: Bytes order (L{BIG_ENDIAN}, L{LITTLE_ENDIAN} or L{MIDDLE_ENDIAN}).
+      Optional if the field set has a parent ;
     - static_size: (optional) Size of FieldSet in bits. This attribute should
       be used in parser of constant size.
 

hachoir-core/hachoir_core/field/parser.py

-from hachoir_core.endian import BIG_ENDIAN, LITTLE_ENDIAN
+from hachoir_core.endian import BIG_ENDIAN, LITTLE_ENDIAN, MIDDLE_ENDIAN
 from hachoir_core.field import GenericFieldSet
 from hachoir_core.log import Logger
 import hachoir_core.config as config
     """
     A parser is the root of all other fields. It create first level of fields
     and have special attributes and methods:
-    - endian: Byte order (L{BIG_ENDIAN} or L{LITTLE_ENDIAN}) of input data ;
+    - endian: Byte order (L{BIG_ENDIAN}, L{LITTLE_ENDIAN} or L{MIDDLE_ENDIAN}) of input data ;
     - stream: Data input stream (set in L{__init__()}) ;
     - size: Field set size will be size of input stream.
     """
         """
         # Check arguments
         assert hasattr(self, "endian") \
-            and self.endian in (BIG_ENDIAN, LITTLE_ENDIAN)
+            and self.endian in (BIG_ENDIAN, LITTLE_ENDIAN, MIDDLE_ENDIAN)
 
         # Call parent constructor
         GenericFieldSet.__init__(self, None, "root", stream, description, stream.askSize(self))

hachoir-core/hachoir_core/stream/input.py

-from hachoir_core.endian import BIG_ENDIAN, LITTLE_ENDIAN
+from hachoir_core.endian import BIG_ENDIAN, LITTLE_ENDIAN, MIDDLE_ENDIAN
 from hachoir_core.error import info
 from hachoir_core.log import Logger
 from hachoir_core.bits import str2long
 from hachoir_core.i18n import getTerminalCharset
 from hachoir_core.tools import lowerBound
 from hachoir_core.i18n import _
+from hachoir_core.tools import alignValue
 from errno import ESPIPE
 from weakref import ref as weakref_ref
 from hachoir_core.stream import StreamError
         raise NotImplementedError
 
     def readBits(self, address, nbits, endian):
-        assert endian in (BIG_ENDIAN, LITTLE_ENDIAN)
+        assert endian in (BIG_ENDIAN, LITTLE_ENDIAN, MIDDLE_ENDIAN)
 
-        shift, data, missing = self.read(address, nbits)
+        if endian is MIDDLE_ENDIAN:
+            # read an aligned chunk of words
+            wordaddr, remainder = divmod(address, 16)
+            wordnbits = alignValue(remainder+nbits, 16)
+            _, data, missing = self.read(wordaddr*16, wordnbits)
+            shift = remainder
+        else:
+            shift, data, missing = self.read(address, nbits)
         if missing:
             raise ReadStreamError(nbits, address)
         value = str2long(data, endian)
-        if endian is BIG_ENDIAN:
+        if endian in (BIG_ENDIAN, MIDDLE_ENDIAN):
             value >>= len(data) * 8 - shift - nbits
         else:
             value >>= shift

hachoir-core/hachoir_core/stream/output.py

 from cStringIO import StringIO
-from hachoir_core.endian import BIG_ENDIAN
+from hachoir_core.endian import BIG_ENDIAN, LITTLE_ENDIAN
 from hachoir_core.bits import long2raw
 from hachoir_core.stream import StreamError
 from errno import EBADF
     filename = property(_getFilename)
 
     def writeBit(self, state, endian):
+        assert endian in (BIG_ENDIAN, LITTLE_ENDIAN) # middle endian not yet supported
         if self._bit_pos == 7:
             self._bit_pos = 0
             if state:
             self._bit_pos += 1
 
     def writeBits(self, count, value, endian):
+        assert endian in (BIG_ENDIAN, LITTLE_ENDIAN) # middle endian not yet supported
         assert 0 <= value < 2**count
 
         # Feed bits to align to byte address