zero-string.scm 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109
  1. ;;; Disarchive
  2. ;;; Copyright © 2020 Ludovic Courtès <ludo@gnu.org>
  3. ;;; Copyright © 2020, 2021 Timothy Sample <samplet@ngyro.com>
  4. ;;;
  5. ;;; This file is part of Disarchive.
  6. ;;;
  7. ;;; Disarchive is free software: you can redistribute it and/or modify
  8. ;;; it under the terms of the GNU General Public License as published by
  9. ;;; the Free Software Foundation, either version 3 of the License, or
  10. ;;; (at your option) any later version.
  11. ;;;
  12. ;;; Disarchive is distributed in the hope that it will be useful,
  13. ;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. ;;; GNU General Public License for more details.
  16. ;;;
  17. ;;; You should have received a copy of the GNU General Public License
  18. ;;; along with Disarchive. If not, see <http://www.gnu.org/licenses/>.
  19. (define-module (disarchive kinds zero-string)
  20. #:use-module (disarchive kinds binary-string)
  21. #:use-module (disarchive serialization)
  22. #:use-module (disarchive utils)
  23. #:use-module (ice-9 match)
  24. #:use-module (rnrs bytevectors)
  25. #:use-module (srfi srfi-9 gnu)
  26. #:export (<zero-string>
  27. make-zero-string
  28. zero-string?
  29. zero-string-value
  30. zero-string-trailer
  31. valid-zero-string?
  32. decode-zero-string
  33. encode-zero-string
  34. -zero-string-))
  35. ;;; Commentary:
  36. ;;;
  37. ;;; A zero string represents a fixed-length, null-terminated binary
  38. ;;; string. It does this with two fields, "value" and "trailer". The
  39. ;;; "value" field is a binary string made up of the bytes before the
  40. ;;; first null byte (or all the bytes if there is no null byte). The
  41. ;;; "trailer" field is a binary string made up of the null byte and
  42. ;;; all of the bytes after it. If the trailer field is entirely null
  43. ;;; bytes, it is represented as the null string ("").
  44. ;;;
  45. ;;; Code:
  46. (define-immutable-record-type <zero-string>
  47. (make-zero-string value trailer)
  48. zero-string?
  49. (value zero-string-value)
  50. (trailer zero-string-trailer))
  51. (define (valid-zero-string? zstr)
  52. "Check that ZSTR satisfies the constraints of a zero string."
  53. ;; The value field must not contain any zeros ('#\nul' for strings
  54. ;; and '0' for bytevectors).
  55. (match zstr
  56. (($ <zero-string>
  57. (and (? valid-binary-string?)
  58. (? no-null-binary-string?))
  59. (? valid-binary-string?))
  60. #t)
  61. (_ #f)))
  62. (define* (decode-zero-string bv #:optional (start 0)
  63. (end (bytevector-length bv)))
  64. "Decode the contents of the bytevector BV as a zero string.
  65. Optionally, START and END indexes can be provided to decode only a
  66. part of BV."
  67. (let* ((k (or (bytevector-index bv 0 start end) end))
  68. (trailer (if (bytevector-zero? bv k end)
  69. ""
  70. (decode-binary-string bv (1+ k) end))))
  71. (make-zero-string (decode-binary-string bv start k)
  72. trailer)))
  73. (define* (encode-zero-string zstr #:optional bv (start 0) end)
  74. "Encode the zero string ZSTR. If BV is set, the result will be
  75. written into BV. Otherwise, the result will be written into a new
  76. bytevector. If you are providing a bytevector, you can also provide
  77. START and END indexes to control where the result is written."
  78. (match zstr
  79. (($ <zero-string> str trailer)
  80. (let* ((str-len (binary-string-length str))
  81. (trailer-start (+ start str-len 1))
  82. (trailer-len (binary-string-length trailer))
  83. (bv (or bv (make-bytevector (+ str-len 1 trailer-len))))
  84. (end (or end (bytevector-length bv))))
  85. (encode-binary-string str bv start end)
  86. ;; Note that 'encode-binary-string' zeros out the rest of the
  87. ;; bytevector up to the end index. This means that we can
  88. ;; ignore null trailers, since the zeros are already there.
  89. (unless (or (zero? trailer-len) (>= trailer-start end))
  90. (encode-binary-string trailer bv trailer-start end))
  91. bv))
  92. (_ (scm-error 'wrong-type-arg 'encode-zero-string
  93. (string-append "Wrong type argument in position 1 "
  94. "(expecting zero-string): ~A")
  95. (list zstr) (list zstr)))))
  96. (define -zero-string-
  97. (make-record-serializer
  98. make-zero-string
  99. `((value ,zero-string-value ,-binary-string-)
  100. (trailer ,zero-string-trailer ,-binary-string-))
  101. #:elide-first-field? #t))