1# Copyright 2019 Google LLC.
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7#     https://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
15
16"""Module providing conversion functions like long to bytes or bytes to long."""
17
18import operator
19import struct
20
21import six
22
23
24def _PadZeroBytes(byte_str, blocksize):
25  """Pads the front of byte_str with binary zeros.
26
27  Args:
28    byte_str: byte string to pad the binary zeros.
29    blocksize: the byte_str will be padded so that the length of the output will
30      be a multiple of blocksize.
31
32  Returns:
33    a new byte string padded with binary zeros if necessary.
34  """
35  if len(byte_str) % blocksize:
36    return (blocksize - len(byte_str) % blocksize) * b'\000' + byte_str
37  return byte_str
38
39
40def LongToBytes(number: int, blocksize: int = 0) -> bytes:
41  """Converts an arbitrary length number to a byte string.
42
43  Args:
44    number: number to convert to bytes.
45    blocksize: if specified, the output bytes length will be a multiple of
46      blocksize.
47
48  Returns:
49    byte string for the number.
50
51  Raises:
52    ValueError: when the number is negative.
53  """
54  if number < 0:
55    raise ValueError('number needs to be >=0, given: {}'.format(number))
56  number_32bitunit_components = []
57  while number != 0:
58    number_32bitunit_components.insert(0, number & 0xFFFFFFFF)
59    number >>= 32
60  converter = struct.Struct('>' + str(len(number_32bitunit_components)) + 'I')
61  n_bytes = six.ensure_binary(converter.pack(*number_32bitunit_components))
62  for idx in range(len(n_bytes)):
63    if operator.getitem(n_bytes, idx) != 0:
64      break
65  else:
66    n_bytes = b'\000'
67    idx = 0
68  n_bytes = n_bytes[idx:]
69  if blocksize > 0:
70    n_bytes = _PadZeroBytes(n_bytes, blocksize)
71  return six.ensure_binary(n_bytes)
72
73
74def BytesToLong(byte_string: bytes) -> int:
75  """Converts given byte string to a long."""
76  result = 0
77  padded_byte_str = _PadZeroBytes(byte_string, 4)
78  component_length = len(padded_byte_str) // 4
79  converter = struct.Struct('>' + str(component_length) + 'I')
80  unpacked_data = converter.unpack(padded_byte_str)
81  for i in range(0, component_length):
82    result += unpacked_data[i] << (32 * (component_length - i - 1))
83  return result
84