You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

133 lines
4.2 KiB

  1. /* bytes to hex implementation */
  2. #include "Python.h"
  3. #include "pystrhex.h"
  4. static PyObject *_Py_strhex_impl(const char* argbuf, const Py_ssize_t arglen,
  5. const PyObject* sep, int bytes_per_sep_group,
  6. const int return_bytes)
  7. {
  8. PyObject *retval;
  9. Py_UCS1* retbuf;
  10. Py_ssize_t i, j, resultlen = 0;
  11. Py_UCS1 sep_char = 0;
  12. unsigned int abs_bytes_per_sep;
  13. if (sep) {
  14. Py_ssize_t seplen = PyObject_Length((PyObject*)sep);
  15. if (seplen < 0) {
  16. return NULL;
  17. }
  18. if (seplen != 1) {
  19. PyErr_SetString(PyExc_ValueError, "sep must be length 1.");
  20. return NULL;
  21. }
  22. if (PyUnicode_Check(sep)) {
  23. if (PyUnicode_READY(sep))
  24. return NULL;
  25. if (PyUnicode_KIND(sep) != PyUnicode_1BYTE_KIND) {
  26. PyErr_SetString(PyExc_ValueError, "sep must be ASCII.");
  27. return NULL;
  28. }
  29. sep_char = PyUnicode_READ_CHAR(sep, 0);
  30. } else if (PyBytes_Check(sep)) {
  31. sep_char = PyBytes_AS_STRING(sep)[0];
  32. } else {
  33. PyErr_SetString(PyExc_TypeError, "sep must be str or bytes.");
  34. return NULL;
  35. }
  36. if (sep_char > 127 && !return_bytes) {
  37. PyErr_SetString(PyExc_ValueError, "sep must be ASCII.");
  38. return NULL;
  39. }
  40. } else {
  41. bytes_per_sep_group = 0;
  42. }
  43. assert(arglen >= 0);
  44. abs_bytes_per_sep = abs(bytes_per_sep_group);
  45. if (bytes_per_sep_group && arglen > 0) {
  46. /* How many sep characters we'll be inserting. */
  47. resultlen = (arglen - 1) / abs_bytes_per_sep;
  48. }
  49. /* Bounds checking for our Py_ssize_t indices. */
  50. if (arglen >= PY_SSIZE_T_MAX / 2 - resultlen) {
  51. return PyErr_NoMemory();
  52. }
  53. resultlen += arglen * 2;
  54. if ((size_t)abs_bytes_per_sep >= (size_t)arglen) {
  55. bytes_per_sep_group = 0;
  56. abs_bytes_per_sep = 0;
  57. }
  58. if (return_bytes) {
  59. /* If _PyBytes_FromSize() were public we could avoid malloc+copy. */
  60. retbuf = (Py_UCS1*) PyMem_Malloc(resultlen);
  61. if (!retbuf)
  62. return PyErr_NoMemory();
  63. retval = NULL; /* silence a compiler warning, assigned later. */
  64. } else {
  65. retval = PyUnicode_New(resultlen, 127);
  66. if (!retval)
  67. return NULL;
  68. retbuf = PyUnicode_1BYTE_DATA(retval);
  69. }
  70. /* Hexlify */
  71. for (i=j=0; i < arglen; ++i) {
  72. assert(j < resultlen);
  73. unsigned char c;
  74. c = (argbuf[i] >> 4) & 0xf;
  75. retbuf[j++] = Py_hexdigits[c];
  76. c = argbuf[i] & 0xf;
  77. retbuf[j++] = Py_hexdigits[c];
  78. if (bytes_per_sep_group && i < arglen - 1) {
  79. Py_ssize_t anchor;
  80. anchor = (bytes_per_sep_group > 0) ? (arglen - 1 - i) : (i + 1);
  81. if (anchor % abs_bytes_per_sep == 0) {
  82. retbuf[j++] = sep_char;
  83. }
  84. }
  85. }
  86. assert(j == resultlen);
  87. if (return_bytes) {
  88. retval = PyBytes_FromStringAndSize((const char *)retbuf, resultlen);
  89. PyMem_Free(retbuf);
  90. }
  91. #ifdef Py_DEBUG
  92. else {
  93. assert(_PyUnicode_CheckConsistency(retval, 1));
  94. }
  95. #endif
  96. return retval;
  97. }
  98. PyObject * _Py_strhex(const char* argbuf, const Py_ssize_t arglen)
  99. {
  100. return _Py_strhex_impl(argbuf, arglen, NULL, 0, 0);
  101. }
  102. /* Same as above but returns a bytes() instead of str() to avoid the
  103. * need to decode the str() when bytes are needed. */
  104. PyObject * _Py_strhex_bytes(const char* argbuf, const Py_ssize_t arglen)
  105. {
  106. return _Py_strhex_impl(argbuf, arglen, NULL, 0, 1);
  107. }
  108. /* These variants include support for a separator between every N bytes: */
  109. PyObject * _Py_strhex_with_sep(const char* argbuf, const Py_ssize_t arglen, const PyObject* sep, const int bytes_per_group)
  110. {
  111. return _Py_strhex_impl(argbuf, arglen, sep, bytes_per_group, 0);
  112. }
  113. /* Same as above but returns a bytes() instead of str() to avoid the
  114. * need to decode the str() when bytes are needed. */
  115. PyObject * _Py_strhex_bytes_with_sep(const char* argbuf, const Py_ssize_t arglen, const PyObject* sep, const int bytes_per_group)
  116. {
  117. return _Py_strhex_impl(argbuf, arglen, sep, bytes_per_group, 1);
  118. }