import struct from cStringIO import StringIO import math _scalarMagic = float.fromhex('0x1.62e42fefa39efp+2') def loads( sIn ): return load( StringIO( sIn ) ) def dumps( obj ): sTmp = StringIO() dump( obj, sTmp ) return sTmp.getvalue() def load( sIn ): # Scan for a valid packet header while True: version = sIn.read(1) if len(version) == 0: return None version = ord(version[0]) if version != 0: break if version == 1: size = struct.unpack('>I', sIn.read(4) )[0] return _readObj( sIn ) def dump( obj, sOut ): sTmp = StringIO() _writeObj( obj, sTmp ) sCore = sTmp.getvalue() sOut.write( struct.pack('>BI', 1, len(sCore)+5 ) ) sOut.write( sCore ) def _readObj( sIn ): t = sIn.read( 1 ) if t == 'i': # Integer return _readPackedInt( sIn ) elif t == 's': # String lng = _readPackedInt( sIn ) return sIn.read( lng ) elif t == '0': # Boolean false return False elif t == '1': # Boolean true return True elif t == 'l': # List ret = [] while True: value = _readObj( sIn ) if value is None: return ret ret.append( value ) elif t == 'd': # Dictionary ret = {} while True: key = _readObj( sIn ) if key is None: return ret if not isinstance( key, str ): raise Exception('Only strings can be used as keys in gats dictionaries') value = _readObj( sIn ); ret[key] = value elif t == 'f': # Float pLng = _readPackedInt( sIn ) bNeg = False if pLng < 0: bNeg = True pLng = -pLng dValue = 0.0 dat = sIn.read( pLng ) for i in xrange(len(dat)-1,0,-1): dValue = (dValue+ord(dat[i]))*(1.0/256.0) dValue += ord(dat[0]) iScale = _readPackedInt( sIn ) dValue *= pow( 256.0, iScale ) if bNeg: return -dValue return dValue elif t == 'F': # Exceptional float st = sIn.read(1) if st == 'N': return float('-nan') elif st == 'n': return float('nan') elif st == 'I': return float('-inf') elif st == 'i': return float('inf') elif st == 'Z': return -0.0 elif st == 'z': return 0.0 else: raise Exception('Invalid exceptional float subtype found.') elif t == 'e': # End marker return None else: raise Exception('Invalid gats type discovered: ' + t) return 'not implemented yet'; def _writeObj( obj, sOut ): if isinstance( obj, int ): sOut.write('i') _writePackedInt( obj, sOut ) elif isinstance( obj, str ): sOut.write('s') _writePackedInt( len(obj), sOut ) sOut.write( obj ) elif isinstance( obj, list ) or isinstance( obj, tuple ): sOut.write('l') for s in obj: _writeObj( s, sOut ) sOut.write('e') elif isinstance( obj, dict ): sOut.write('d') for key in obj.iterkeys(): _writeObj( key, sOut ) _writeObj( obj[key], sOut ) sOut.write('e') elif isinstance( obj, bool ): if obj == True: sOut.write('1') else: sOut.write('0') elif isinstance( obj, float ): if math.isnan( obj ): if math.copysign( 1.0, obj ) < 0.0: sOut.write('FN') else: sOut.write('Fn') elif math.isinf( obj ): if math.copysign( 1.0, obj ) < 0.0: sOut.write('FI') else: sOut.write('Fi') elif obj == 0.0: if math.copysign( 1.0, obj ) < 0.0: sOut.write('FZ') else: sOut.write('Fz') else: sOut.write('f') d = obj bNeg = False if d < 0.0: bNeg = True d = -d iScale = int(math.log( d ) / _scalarMagic) if iScale < 0: iScale -= 1 sTmp = StringIO() d /= pow( 256.0, iScale ) sTmp.write( chr( int(d) ) ) d -= int(d) for j in xrange( 0, 150 ): d = d*256.0 sTmp.write( chr(int(d)) ) d -= int(d) if d == 0.0: break sTmp = sTmp.getvalue() if bNeg: _writePackedInt( -len(sTmp), sOut ) else: _writePackedInt( len(sTmp), sOut ) sOut.write( sTmp ) _writePackedInt( iScale, sOut ) else: raise Exception('A type that is not gats-encodable was encountered: ' + str(type(obj))) def _readPackedInt( sIn ): bNeg = False b = ord(sIn.read(1)[0]) bNeg = (b&0x40) == 0x40; rOut = b&0x3F; c = 0 while (b&0x80) == 0x80: b = ord(sIn.read(1)[0]) rOut |= (b&0x7F)<<(6+7*c) c += 1 if bNeg: return -rOut return rOut def _writePackedInt( iIn, sOut ): if iIn < 0: iIn = -iIn b = iIn&0x3F if iIn > b: b |= 0x80 | 0x40 else: b |= 0x40 else: b = iIn&0x3F if iIn > b: b |= 0x80 sOut.write( chr( b ) ) iIn = iIn >> 6 while iIn > 0: b = iIn&0x7F if iIn > b: b |= 0x80 sOut.write( chr( b ) ) iIn = iIn >> 7