text: # 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/Output utilities."""
  
  import struct
  import avro.schema as schema
  
  #Constants
  _INT_MIN_VALUE = -(1 << 31)
  _INT_MAX_VALUE = (1 << 31) - 1
  _LONG_MIN_VALUE = -(1 << 63)
  _LONG_MAX_VALUE = (1 << 63) - 1
  _STRUCT_INT = struct.Struct('!I')
  _STRUCT_LONG = struct.Struct('!Q')
  _STRUCT_FLOAT = struct.Struct('!f')
  _STRUCT_DOUBLE = struct.Struct('!d')
  
  class AvroTypeException(schema.AvroException):
    """Raised when illegal type is used."""
    def __init__(self, schm=None, datum=None, msg=None):
      if msg is None:
        msg = "Not a "+schema.stringval(schm)+": "+datum.__str__()
      schema.AvroException.__init__(self, msg)
  
  class DatumReaderBase(object):
    """Base class for reading data of a schema."""
  
    def setschema(self, schema):
      pass
  
    def read(self, decoder):
      """Read a datum. Traverse the schema, depth-first, reading all leaf values
      in the schema into a datum that is returned"""
      pass
  
  class DatumWriterBase(object):
    """Base class for writing data of a schema."""
    def setschema(self, schema):
      pass
  
    def write(self, data, encoder):
      """Write a datum. Traverse the schema, depth first, writing each leaf value
      in the schema from the datum to the output."""
      pass
  
  
  class Decoder(object):
    """Read leaf values."""
  
    def __init__(self, reader):
      self.__reader = reader
  
    def readboolean(self):
      return ord(self.__reader.read(1)) == 1
  
    def readint(self):
      return self.readlong()
  
    def readlong(self):
      b = ord(self.__reader.read(1))
      n = b & 0x7F
      shift = 7
      while (b & 0x80) != 0:
        b = ord(self.__reader.read(1))
        n |= (b & 0x7F) << shift
        shift += 7
      datum = (n >> 1) ^ -(n & 1)
      return datum
  
    def readfloat(self):
      bits = (((ord(self.__reader.read(1)) & 0xffL)    ) |
          ((ord(self.__reader.read(1)) & 0xffL) <<  8) |
          ((ord(self.__reader.read(1)) & 0xffL) << 16) |
          ((ord(self.__reader.read(1)) & 0xffL) << 24))
      return _STRUCT_FLOAT.unpack(_STRUCT_INT.pack(bits))[0]
  
    def readdouble(self):
      bits = (((ord(self.__reader.read(1)) & 0xffL)    ) |
          ((ord(self.__reader.read(1)) & 0xffL) <<  8) |
          ((ord(self.__reader.read(1)) & 0xffL) << 16) |
          ((ord(self.__reader.read(1)) & 0xffL) << 24) |
          ((ord(self.__reader.read(1)) & 0xffL) << 32) |
          ((ord(self.__reader.read(1)) & 0xffL) << 40) |
          ((ord(self.__reader.read(1)) & 0xffL) << 48) |
          ((ord(self.__reader.read(1)) & 0xffL) << 56))
      return _STRUCT_DOUBLE.unpack(_STRUCT_LONG.pack(bits))[0]
  
    def readbytes(self):
      return self.read(self.readlong())
  
    def readutf8(self):
      return unicode(self.readbytes(), "utf-8")
  
    def read(self, len):
      return struct.unpack(len.__str__()+'s', self.__reader.read(len))[0]
  
    def skipboolean(self):
      self.skip(1)
  
    def skipint(self):
      self.skip(4)
  
    def skiplong(self):
      self.skip(8)
  
    def skipfloat(self):
      self.skip(4)
  
    def skipdouble(self):
      self.skip(8)
  
    def skipbytes(self):
      self.skip(self.readlong())
  
    def skiputf8(self):
      self.skipbytes()
  
    def skip(self, len):
      self.__reader.seek(self.__reader.tell()+len)
  
  class Encoder(object):
    """Write leaf values."""
  
    def __init__(self, writer):
      self.__writer = writer
    
    def writeboolean(self, datum):
      if not isinstance(datum, bool):
        raise AvroTypeException(schema.BOOLEAN, datum)
      if datum:
        self.__writer.write(chr(1))
      else:
        self.__writer.write(chr(0))
  
    def writeint(self, n):
      if n < _INT_MIN_VALUE or n > _INT_MAX_VALUE:
        raise AvroTypeException(schema.INT, n, 
                                "datam too big to fit into avro int")
      self.writelong(n);
  
    def writelong(self, n):
      if n < _LONG_MIN_VALUE or n > _LONG_MAX_VALUE:
        raise AvroTypeException(schema.LONG, n, 
                                "datam too big to fit into avro long")
      n = (n << 1) ^ (n >> 63)
      while (n & ~0x7F) != 0:
        self.__writer.write(chr((n & 0x7f) | 0x80))
        n >>=7
      self.__writer.write(chr(n))
  
    def writefloat(self, datum):
      bits = _STRUCT_INT.unpack(_STRUCT_FLOAT.pack(datum))[0]
      self.__writer.write(chr((bits    ) & 0xFF))
      self.__writer.write(chr((bits >>  8) & 0xFF))
      self.__writer.write(chr((bits >>  16) & 0xFF))
      self.__writer.write(chr((bits >>  24) & 0xFF))
  
    def writedouble(self, datum):
      bits = _STRUCT_LONG.unpack(_STRUCT_DOUBLE.pack(datum))[0]
      self.__writer.write(chr((bits    ) & 0xFF))
      self.__writer.write(chr((bits >>  8) & 0xFF))
      self.__writer.write(chr((bits >>  16) & 0xFF))
      self.__writer.write(chr((bits >>  24) & 0xFF))
      self.__writer.write(chr((bits >>  32) & 0xFF))
      self.__writer.write(chr((bits >>  40) & 0xFF))
      self.__writer.write(chr((bits >>  48) & 0xFF))
      self.__writer.write(chr((bits >>  56) & 0xFF))
  
    def writebytes(self, datum):
      if not isinstance(datum, str):
        raise AvroTypeException(schema.BYTES, datum, 
                                "avro BYTES should be python str")
      self.writelong(len(datum))
      self.__writer.write(struct.pack(len(datum).__str__()+'s',datum))
  
    def writeutf8(self, datum):
      if not isinstance(datum, basestring):
        raise AvroTypeException(schema.STRING, datum, 
                                "avro STRING should be python unicode")
      datum = datum.encode("utf-8")
      self.writebytes(datum)
  
    def write(self, datum):
      self.__writer.write(datum)
  
  