# Copyright 2009-2012 10gen, Inc. # # Licensed 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. """BSON (Binary JSON) encoding and decoding. """ import calendar import datetime import re import struct import sys from asyncio_mongo._bson.binary import (Binary, OLD_UUID_SUBTYPE, JAVA_LEGACY, CSHARP_LEGACY) from asyncio_mongo._bson.code import Code from asyncio_mongo._bson.dbref import DBRef from asyncio_mongo._bson.errors import (InvalidBSON, InvalidDocument, InvalidStringData) from asyncio_mongo._bson.max_key import MaxKey from asyncio_mongo._bson.min_key import MinKey from asyncio_mongo._bson.objectid import ObjectId from asyncio_mongo._bson.py3compat import b, binary_type from asyncio_mongo._bson.son import SON, RE_TYPE from asyncio_mongo._bson.timestamp import Timestamp from asyncio_mongo._bson.tz_util import utc try: from asyncio_mongo._bson import _cbson _use_c = True except ImportError: _use_c = False try: import uuid _use_uuid = True except ImportError: _use_uuid = False PY3 = sys.version_info[0] == 3 MAX_INT32 = 2147483647 MIN_INT32 = -2147483648 MAX_INT64 = 9223372036854775807 MIN_INT64 = -9223372036854775808 EPOCH_AWARE = datetime.datetime.fromtimestamp(0, utc) EPOCH_NAIVE = datetime.datetime.utcfromtimestamp(0) # Create constants compatible with all versions of # python from 2.4 forward. In 2.x b("foo") is just # "foo". In 3.x it becomes b"foo". EMPTY = b("") ZERO = b("\x00") ONE = b("\x01") BSONNUM = b("\x01") # Floating point BSONSTR = b("\x02") # UTF-8 string BSONOBJ = b("\x03") # Embedded document BSONARR = b("\x04") # Array BSONBIN = b("\x05") # Binary BSONUND = b("\x06") # Undefined BSONOID = b("\x07") # ObjectId BSONBOO = b("\x08") # Boolean BSONDAT = b("\x09") # UTC Datetime BSONNUL = b("\x0A") # Null BSONRGX = b("\x0B") # Regex BSONREF = b("\x0C") # DBRef BSONCOD = b("\x0D") # Javascript code BSONSYM = b("\x0E") # Symbol BSONCWS = b("\x0F") # Javascript code with scope BSONINT = b("\x10") # 32bit int BSONTIM = b("\x11") # Timestamp BSONLON = b("\x12") # 64bit int BSONMIN = b("\xFF") # Min key BSONMAX = b("\x7F") # Max key def _get_int(data, position, as_class=None, tz_aware=False, uuid_subtype=OLD_UUID_SUBTYPE, unsigned=False): format = unsigned and "I" or "i" try: value = struct.unpack("<%s" % format, data[position:position + 4])[0] except struct.error: raise InvalidBSON() position += 4 return value, position def _get_c_string(data, position, length=None): if length is None: try: end = data.index(ZERO, position) except ValueError: raise InvalidBSON() else: end = position + length value = data[position:end].decode("utf-8") position = end + 1 return value, position def _make_c_string(string, check_null=False): if isinstance(string, str): if check_null and "\x00" in string: raise InvalidDocument("BSON keys / regex patterns must not " "contain a NULL character") return string.encode("utf-8") + ZERO else: if check_null and ZERO in string: raise InvalidDocument("BSON keys / regex patterns must not " "contain a NULL character") try: string.decode("utf-8") return string + ZERO except UnicodeError: raise InvalidStringData("strings in documents must be valid " "UTF-8: %r" % string) def _get_number(data, position, as_class, tz_aware, uuid_subtype): num = struct.unpack(" MAX_INT64 or value < MIN_INT64: raise OverflowError("BSON can only handle up to 8-byte ints") if value > MAX_INT32 or value < MIN_INT32: return BSONLON + name + struct.pack(" MAX_INT64 or value < MIN_INT64: raise OverflowError("BSON can only handle up to 8-byte ints") return BSONLON + name + struct.pack("