PageRenderTime 47ms CodeModel.GetById 25ms RepoModel.GetById 0ms app.codeStats 0ms

/pypy/module/binascii/interp_base64.py

https://bitbucket.org/pypy/pypy/
Python | 113 lines | 97 code | 9 blank | 7 comment | 11 complexity | 9ed784631e41d6bac3bf7147dc94ad7e MD5 | raw file
Possible License(s): AGPL-3.0, BSD-3-Clause, Apache-2.0
  1. from pypy.interpreter.error import OperationError
  2. from pypy.interpreter.gateway import unwrap_spec
  3. from rpython.rlib.rstring import StringBuilder
  4. from pypy.module.binascii.interp_binascii import raise_Error
  5. from rpython.rlib.rarithmetic import ovfcheck
  6. # ____________________________________________________________
  7. PAD = '='
  8. table_a2b_base64 = [
  9. -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
  10. -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
  11. -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,62, -1,-1,-1,63,
  12. 52,53,54,55, 56,57,58,59, 60,61,-1,-1, -1,-1,-1,-1, # Note PAD->-1 here
  13. -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11,12,13,14,
  14. 15,16,17,18, 19,20,21,22, 23,24,25,-1, -1,-1,-1,-1,
  15. -1,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40,
  16. 41,42,43,44, 45,46,47,48, 49,50,51,-1, -1,-1,-1,-1,
  17. -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
  18. -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
  19. -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
  20. -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
  21. -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
  22. -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
  23. -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
  24. -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
  25. ]
  26. def _transform(n):
  27. if n == -1:
  28. return '\xff'
  29. else:
  30. return chr(n)
  31. table_a2b_base64 = ''.join(map(_transform, table_a2b_base64))
  32. assert len(table_a2b_base64) == 256
  33. @unwrap_spec(ascii='bufferstr')
  34. def a2b_base64(space, ascii):
  35. "Decode a line of base64 data."
  36. res = StringBuilder((len(ascii) // 4) * 3) # maximum estimate
  37. quad_pos = 0
  38. leftchar = 0
  39. leftbits = 0
  40. last_char_was_a_pad = False
  41. for c in ascii:
  42. if c == PAD:
  43. if quad_pos > 2 or (quad_pos == 2 and last_char_was_a_pad):
  44. break # stop on 'xxx=' or on 'xx=='
  45. last_char_was_a_pad = True
  46. else:
  47. n = ord(table_a2b_base64[ord(c)])
  48. if n == 0xff:
  49. continue # ignore strange characters
  50. #
  51. # Shift it in on the low end, and see if there's
  52. # a byte ready for output.
  53. quad_pos = (quad_pos + 1) & 3
  54. leftchar = (leftchar << 6) | n
  55. leftbits += 6
  56. #
  57. if leftbits >= 8:
  58. leftbits -= 8
  59. res.append(chr(leftchar >> leftbits))
  60. leftchar &= ((1 << leftbits) - 1)
  61. #
  62. last_char_was_a_pad = False
  63. else:
  64. if leftbits != 0:
  65. raise_Error(space, "Incorrect padding")
  66. return space.newbytes(res.build())
  67. # ____________________________________________________________
  68. table_b2a_base64 = (
  69. "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/")
  70. @unwrap_spec(bin='bufferstr')
  71. def b2a_base64(space, bin):
  72. "Base64-code line of data."
  73. newlength = (len(bin) + 2) // 3
  74. try:
  75. newlength = ovfcheck(newlength * 4)
  76. except OverflowError:
  77. raise OperationError(space.w_MemoryError, space.w_None)
  78. newlength += 1
  79. res = StringBuilder(newlength)
  80. leftchar = 0
  81. leftbits = 0
  82. for c in bin:
  83. # Shift into our buffer, and output any 6bits ready
  84. leftchar = (leftchar << 8) | ord(c)
  85. leftbits += 8
  86. res.append(table_b2a_base64[(leftchar >> (leftbits-6)) & 0x3f])
  87. leftbits -= 6
  88. if leftbits >= 6:
  89. res.append(table_b2a_base64[(leftchar >> (leftbits-6)) & 0x3f])
  90. leftbits -= 6
  91. #
  92. if leftbits == 2:
  93. res.append(table_b2a_base64[(leftchar & 3) << 4])
  94. res.append(PAD)
  95. res.append(PAD)
  96. elif leftbits == 4:
  97. res.append(table_b2a_base64[(leftchar & 0xf) << 2])
  98. res.append(PAD)
  99. res.append('\n')
  100. return space.newbytes(res.build())