github.com/grumpyhome/grumpy@v0.3.1-0.20201208125205-7b775405bdf1/grumpy-runtime-src/third_party/pypy/_struct.py (about)

     1  #
     2  # This module is a pure Python version of pypy.module.struct.
     3  # It is only imported if the vastly faster pypy.module.struct is not
     4  # compiled in.  For now we keep this version for reference and
     5  # because pypy.module.struct is not ootype-backend-friendly yet.
     6  #
     7  
     8  """Functions to convert between Python values and C structs.
     9  Python strings are used to hold the data representing the C struct
    10  and also as format strings to describe the layout of data in the C struct.
    11  
    12  The optional first format char indicates byte order, size and alignment:
    13   @: native order, size & alignment (default)
    14   =: native order, std. size & alignment
    15   <: little-endian, std. size & alignment
    16   >: big-endian, std. size & alignment
    17   !: same as >
    18  
    19  The remaining chars indicate types of args and must match exactly;
    20  these can be preceded by a decimal repeat count:
    21     x: pad byte (no data);
    22     c:char;
    23     b:signed byte;
    24     B:unsigned byte;
    25     h:short;
    26     H:unsigned short;
    27     i:int;
    28     I:unsigned int;
    29     l:long;
    30     L:unsigned long;
    31     f:float;
    32     d:double.
    33  Special cases (preceding decimal count indicates length):
    34     s:string (array of char); p: pascal string (with count byte).
    35  Special case (only available in native format):
    36     P:an integer type that is wide enough to hold a pointer.
    37  Special case (not in native mode unless 'long long' in platform C):
    38     q:long long;
    39     Q:unsigned long long
    40  Whitespace between formats is ignored.
    41  
    42  The variable struct.error is an exception raised on errors."""
    43  
    44  import math
    45  import sys
    46  
    47  # TODO: XXX Find a way to get information on native sizes and alignments
    48  
    49  
    50  class StructError(Exception):
    51    pass
    52  error = StructError
    53  
    54  bytes = str
    55  
    56  
    57  def unpack_int(data, index, size, le):
    58    _bytes = [b for b in data[index:index + size]]
    59    if le == 'little':
    60      _bytes.reverse()
    61    number = 0
    62    for b in _bytes:
    63      number = number << 8 | b
    64    return int(number)
    65  
    66  
    67  def unpack_signed_int(data, index, size, le):
    68    number = unpack_int(data, index, size, le)
    69    max = (1 << (size * 8))
    70    if number > (1 << (size * 8 - 1)) - 1:
    71      number = int(-1 * (max - number))
    72    return number
    73  
    74  INFINITY = 1e200 * 1e200
    75  NAN = INFINITY / INFINITY
    76  
    77  
    78  def unpack_char(data, index, size, le):
    79    return data[index:index + size]
    80  
    81  
    82  def pack_int(number, size, le):
    83    x = number
    84    res = []
    85    for i in range(size):
    86      res.append(x & 0xff)
    87      x = x >> 8
    88    if le == 'big':
    89      res.reverse()
    90    return ''.join(chr(x) for x in res)
    91  
    92  
    93  def pack_signed_int(number, size, le):
    94    if not isinstance(number, int):
    95      raise StructError("argument for i,I,l,L,q,Q,h,H must be integer")
    96    if number > (1 << (8 * size - 1)) - 1 or number < -1 * (1 << (8 * size - 1)):
    97      raise OverflowError("Number:%i too large to convert" % number)
    98    return pack_int(number, size, le)
    99  
   100  
   101  def pack_unsigned_int(number, size, le):
   102    if not isinstance(number, int):
   103      raise StructError("argument for i,I,l,L,q,Q,h,H must be integer")
   104    if number < 0:
   105      raise TypeError("can't convert negative long to unsigned")
   106    if number > (1 << (8 * size)) - 1:
   107      raise OverflowError("Number:%i too large to convert" % number)
   108    return pack_int(number, size, le)
   109  
   110  
   111  def pack_char(char, size, le):
   112    return str(char)
   113  
   114  
   115  def isinf(x):
   116    return x != 0.0 and x / 2 == x
   117  
   118  
   119  def isnan(v):
   120    return v != v * 1.0 or (v == 1.0 and v == 2.0)
   121  
   122  
   123  def pack_float(x, size, le):
   124    unsigned = float_pack(x, size)
   125    result = []
   126    for i in range(8):
   127      result.append((unsigned >> (i * 8)) & 0xFF)
   128    if le == "big":
   129      result.reverse()
   130    return ''.join(chr(x) for x in result)
   131  
   132  
   133  def unpack_float(data, index, size, le):
   134    binary = [data[i] for i in range(index, index + 8)]
   135    if le == "big":
   136      binary.reverse()
   137    unsigned = 0
   138    for i in range(8):
   139      # unsigned |= binary[i] << (i * 8)
   140      unsigned |= ord(binary[i]) << (i * 8)
   141    return float_unpack(unsigned, size, le)
   142  
   143  
   144  def round_to_nearest(x):
   145    """Python 3 style round:  round a float x to the nearest int, but
   146    unlike the builtin Python 2.x round function:
   147  
   148      - return an int, not a float
   149      - do round-half-to-even, not round-half-away-from-zero.
   150  
   151    We assume that x is finite and nonnegative; except wrong results
   152    if you use this for negative x.
   153  
   154    """
   155    int_part = int(x)
   156    frac_part = x - int_part
   157    if frac_part > 0.5 or frac_part == 0.5 and int_part & 1 == 1:
   158      int_part += 1
   159    return int_part
   160  
   161  
   162  def float_unpack(Q, size, le):
   163    """Convert a 32-bit or 64-bit integer created
   164    by float_pack into a Python float."""
   165  
   166    if size == 8:
   167      MIN_EXP = -1021  # = sys.float_info.min_exp
   168      MAX_EXP = 1024   # = sys.float_info.max_exp
   169      MANT_DIG = 53    # = sys.float_info.mant_dig
   170      BITS = 64
   171    elif size == 4:
   172      MIN_EXP = -125   # C's FLT_MIN_EXP
   173      MAX_EXP = 128    # FLT_MAX_EXP
   174      MANT_DIG = 24    # FLT_MANT_DIG
   175      BITS = 32
   176    else:
   177      raise ValueError("invalid size value")
   178  
   179    if Q >> BITS:
   180      raise ValueError("input out of range")
   181  
   182    # extract pieces
   183    sign = Q >> BITS - 1
   184    exp = (Q & ((1 << BITS - 1) - (1 << MANT_DIG - 1))) >> MANT_DIG - 1
   185    mant = Q & ((1 << MANT_DIG - 1) - 1)
   186  
   187    if exp == MAX_EXP - MIN_EXP + 2:
   188      # nan or infinity
   189      result = float('nan') if mant else float('inf')
   190    elif exp == 0:
   191      # subnormal or zero
   192      result = math.ldexp(float(mant), MIN_EXP - MANT_DIG)
   193    else:
   194      # normal
   195      mant += 1 << MANT_DIG - 1
   196      result = math.ldexp(float(mant), exp + MIN_EXP - MANT_DIG - 1)
   197    return -result if sign else result
   198  
   199  
   200  def float_pack(x, size):
   201    """Convert a Python float x into a 64-bit unsigned integer
   202    with the same byte representation."""
   203  
   204    if size == 8:
   205      MIN_EXP = -1021  # = sys.float_info.min_exp
   206      MAX_EXP = 1024   # = sys.float_info.max_exp
   207      MANT_DIG = 53    # = sys.float_info.mant_dig
   208      BITS = 64
   209    elif size == 4:
   210      MIN_EXP = -125   # C's FLT_MIN_EXP
   211      MAX_EXP = 128    # FLT_MAX_EXP
   212      MANT_DIG = 24    # FLT_MANT_DIG
   213      BITS = 32
   214    else:
   215      raise ValueError("invalid size value")
   216  
   217    sign = math.copysign(1.0, x) < 0.0
   218    if math.isinf(x):
   219      mant = 0
   220      exp = MAX_EXP - MIN_EXP + 2
   221    elif math.isnan(x):
   222      mant = 1 << (MANT_DIG - 2)  # other values possible
   223      exp = MAX_EXP - MIN_EXP + 2
   224    elif x == 0.0:
   225      mant = 0
   226      exp = 0
   227    else:
   228      m, e = math.frexp(abs(x))  # abs(x) == m * 2**e
   229      exp = e - (MIN_EXP - 1)
   230      if exp > 0:
   231        # Normal case.
   232        mant = round_to_nearest(m * (1 << MANT_DIG))
   233        mant -= 1 << MANT_DIG - 1
   234      else:
   235        # Subnormal case.
   236        if exp + MANT_DIG - 1 >= 0:
   237          mant = round_to_nearest(m * (1 << exp + MANT_DIG - 1))
   238        else:
   239          mant = 0
   240        exp = 0
   241  
   242      # Special case: rounding produced a MANT_DIG-bit mantissa.
   243      assert 0 <= mant <= 1 << MANT_DIG - 1
   244      if mant == 1 << MANT_DIG - 1:
   245        mant = 0
   246        exp += 1
   247  
   248      # Raise on overflow (in some circumstances, may want to return
   249      # infinity instead).
   250      if exp >= MAX_EXP - MIN_EXP + 2:
   251        raise OverflowError("float too large to pack in this format")
   252  
   253    # check constraints
   254    assert 0 <= mant < 1 << MANT_DIG - 1
   255    assert 0 <= exp <= MAX_EXP - MIN_EXP + 2
   256    assert 0 <= sign <= 1
   257    return ((sign << BITS - 1) | (exp << MANT_DIG - 1)) | mant
   258  
   259  
   260  big_endian_format = {
   261      'x': {'size': 1, 'alignment': 0, 'pack': None, 'unpack': None},
   262      'b': {'size': 1, 'alignment': 0, 'pack': pack_signed_int, 'unpack': unpack_signed_int},
   263      'B': {'size': 1, 'alignment': 0, 'pack': pack_unsigned_int, 'unpack': unpack_int},
   264      'c': {'size': 1, 'alignment': 0, 'pack': pack_char, 'unpack': unpack_char},
   265      's': {'size': 1, 'alignment': 0, 'pack': None, 'unpack': None},
   266      'p': {'size': 1, 'alignment': 0, 'pack': None, 'unpack': None},
   267      'h': {'size': 2, 'alignment': 0, 'pack': pack_signed_int, 'unpack': unpack_signed_int},
   268      'H': {'size': 2, 'alignment': 0, 'pack': pack_unsigned_int, 'unpack': unpack_int},
   269      'i': {'size': 4, 'alignment': 0, 'pack': pack_signed_int, 'unpack': unpack_signed_int},
   270      'I': {'size': 4, 'alignment': 0, 'pack': pack_unsigned_int, 'unpack': unpack_int},
   271      'l': {'size': 4, 'alignment': 0, 'pack': pack_signed_int, 'unpack': unpack_signed_int},
   272      'L': {'size': 4, 'alignment': 0, 'pack': pack_unsigned_int, 'unpack': unpack_int},
   273      'q': {'size': 8, 'alignment': 0, 'pack': pack_signed_int, 'unpack': unpack_signed_int},
   274      'Q': {'size': 8, 'alignment': 0, 'pack': pack_unsigned_int, 'unpack': unpack_int},
   275      'f': {'size': 4, 'alignment': 0, 'pack': pack_float, 'unpack': unpack_float},
   276      'd': {'size': 8, 'alignment': 0, 'pack': pack_float, 'unpack': unpack_float},
   277  }
   278  default = big_endian_format
   279  formatmode = {'<': (default, 'little'),
   280                '>': (default, 'big'),
   281                '!': (default, 'big'),
   282                '=': (default, sys.byteorder),
   283                '@': (default, sys.byteorder)
   284                }
   285  
   286  
   287  def getmode(fmt):
   288    try:
   289      formatdef, endianness = formatmode[fmt[0]]
   290      index = 1
   291    except (IndexError, KeyError):
   292      formatdef, endianness = formatmode['@']
   293      index = 0
   294    return formatdef, endianness, index
   295  
   296  
   297  def getNum(fmt, i):
   298    num = None
   299    cur = fmt[i]
   300    while ('0' <= cur) and (cur <= '9'):
   301      if num == None:
   302        num = int(cur)
   303      else:
   304        num = 10 * num + int(cur)
   305      i += 1
   306      cur = fmt[i]
   307    return num, i
   308  
   309  
   310  def calcsize(fmt):
   311    """calcsize(fmt) -> int
   312    Return size of C struct described by format string fmt.
   313    See struct.__doc__ for more on format strings."""
   314  
   315    formatdef, endianness, i = getmode(fmt)
   316    num = 0
   317    result = 0
   318    while i < len(fmt):
   319      num, i = getNum(fmt, i)
   320      cur = fmt[i]
   321      try:
   322        format = formatdef[cur]
   323      except KeyError:
   324        raise StructError("%s is not a valid format" % cur)
   325      if num != None:
   326        result += num * format['size']
   327      else:
   328        result += format['size']
   329      num = 0
   330      i += 1
   331    return result
   332  
   333  
   334  def pack(fmt, *args):
   335    """pack(fmt, v1, v2, ...) -> string
   336       Return string containing values v1, v2, ... packed according to fmt.
   337       See struct.__doc__ for more on format strings."""
   338    formatdef, endianness, i = getmode(fmt)
   339    args = list(args)
   340    n_args = len(args)
   341    result = []
   342    while i < len(fmt):
   343      num, i = getNum(fmt, i)
   344      cur = fmt[i]
   345      try:
   346        format = formatdef[cur]
   347      except KeyError:
   348        raise StructError("%s is not a valid format" % cur)
   349      if num == None:
   350        num_s = 0
   351        num = 1
   352      else:
   353        num_s = num
   354  
   355      if cur == 'x':
   356        result += [b'\0' * num]
   357      elif cur == 's':
   358        if isinstance(args[0], bytes):
   359          padding = num - len(args[0])
   360          result += [args[0][:num] + b'\0' * padding]
   361          args.pop(0)
   362        else:
   363          raise StructError("arg for string format not a string")
   364      elif cur == 'p':
   365        if isinstance(args[0], bytes):
   366          padding = num - len(args[0]) - 1
   367  
   368          if padding > 0:
   369            result += [bytes([len(args[0])]) + args[0]
   370                       [:num - 1] + b'\0' * padding]
   371          else:
   372            if num < 255:
   373              result += [bytes([num - 1]) + args[0][:num - 1]]
   374            else:
   375              result += [bytes([255]) + args[0][:num - 1]]
   376          args.pop(0)
   377        else:
   378          raise StructError("arg for string format not a string")
   379  
   380      else:
   381        if len(args) < num:
   382          raise StructError("insufficient arguments to pack")
   383        for var in args[:num]:
   384          result += [format['pack'](var, format['size'], endianness)]
   385        args = args[num:]
   386      num = None
   387      i += 1
   388    if len(args) != 0:
   389      raise StructError("too many arguments for pack format")
   390    return b''.join(result)
   391  
   392  
   393  def unpack(fmt, data):
   394    """unpack(fmt, string) -> (v1, v2, ...)
   395       Unpack the string, containing packed C structure data, according
   396       to fmt.  Requires len(string)==calcsize(fmt).
   397       See struct.__doc__ for more on format strings."""
   398    formatdef, endianness, i = getmode(fmt)
   399    j = 0
   400    num = 0
   401    result = []
   402    length = calcsize(fmt)
   403    if length != len(data):
   404      raise StructError("unpack str size does not match format")
   405    while i < len(fmt):
   406      num, i = getNum(fmt, i)
   407      cur = fmt[i]
   408      i += 1
   409      try:
   410        format = formatdef[cur]
   411      except KeyError:
   412        raise StructError("%s is not a valid format" % cur)
   413  
   414      if not num:
   415        num = 1
   416  
   417      if cur == 'x':
   418        j += num
   419      elif cur == 's':
   420        result.append(data[j:j + num])
   421        j += num
   422      elif cur == 'p':
   423        n = data[j]
   424        if n >= num:
   425          n = num - 1
   426        result.append(data[j + 1:j + n + 1])
   427        j += num
   428      else:
   429        for n in range(num):
   430          result += [format['unpack'](data, j, format['size'], endianness)]
   431          j += format['size']
   432  
   433    return tuple(result)
   434  
   435  
   436  def pack_into(fmt, buf, offset, *args):
   437    data = pack(fmt, *args)
   438    buffer(buf)[offset:offset + len(data)] = data
   439  
   440  
   441  def unpack_from(fmt, buf, offset=0):
   442    size = calcsize(fmt)
   443    data = buffer(buf)[offset:offset + size]
   444    if len(data) != size:
   445      raise error("unpack_from requires a buffer of at least %d bytes"
   446                  % (size,))
   447    return unpack(fmt, data)
   448  
   449  
   450  def _clearcache():
   451    "Clear the internal cache."
   452    # No cache in this implementation