bytevector-string-check.scm 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469
  1. ; Adapted from the R6RS reference implementation, which is:
  2. ; Copyright 2007 William D Clinger.
  3. ; Permission to copy this software, in whole or in part, to use this
  4. ; software for any lawful purpose, and to redistribute this software
  5. ; is granted subject to the restriction that all copies made of this
  6. ; software must include this copyright notice in full.
  7. ;
  8. ; I also request that you send me a copy of any improvements that you
  9. ; make to this software so that they may be incorporated within it to
  10. ; the benefit of the Scheme community.
  11. (define *random-stress-tests* 100) ; number of tests
  12. (define *random-stress-test-max-size* 50) ; twice average size of string
  13. (define (test-roundtrip bvec tostring tobvec)
  14. (let* ((s1 (tostring bvec))
  15. (b2 (tobvec s1))
  16. (s2 (tostring b2)))
  17. (check (string=? s1 s2) => #t)))
  18. (define random
  19. (letrec ((random14
  20. (lambda (n)
  21. (set! x (remainder (+ (* a x) c) (+ m 1)))
  22. (remainder (quotient x 8) n)))
  23. (a 701)
  24. (x 1)
  25. (c 743483)
  26. (m 524287)
  27. (loop
  28. (lambda (q r n)
  29. (if (zero? q)
  30. (remainder r n)
  31. (loop (quotient q 16384)
  32. (+ (* 16384 r) (random14 16384))
  33. n)))))
  34. (lambda (n)
  35. (if (< n 16384)
  36. (random14 n)
  37. (loop (quotient n 16384) (random14 16384) n)))))
  38. ; Returns a random bytevector of length up to n.
  39. (define (random-bytevector n)
  40. (let* ((n (random n))
  41. (bv (make-bytevector n)))
  42. (do ((i 0 (+ i 1)))
  43. ((= i n) bv)
  44. (bytevector-u8-set! bv i (random 256)))))
  45. ; Returns a random bytevector of even length up to n.
  46. (define (random-bytevector2 n)
  47. (let* ((n (random n))
  48. (n (if (odd? n) (+ n 1) n))
  49. (bv (make-bytevector n)))
  50. (do ((i 0 (+ i 1)))
  51. ((= i n) bv)
  52. (bytevector-u8-set! bv i (random 256)))))
  53. ; Returns a random bytevector of multiple-of-4 length up to n.
  54. (define (random-bytevector4 n)
  55. (let* ((n (random n))
  56. (n (* 4 (round (/ n 4))))
  57. (bv (make-bytevector n)))
  58. (do ((i 0 (+ i 1)))
  59. ((= i n) bv)
  60. (bytevector-u8-set! bv i (random 256)))))
  61. (define (test-char-range lo hi tostring tobytevector)
  62. (let* ((n (+ 1 (- hi lo)))
  63. (s (make-string n))
  64. (replacement-character (integer->char #xfffd)))
  65. (do ((i lo (+ i 1)))
  66. ((> i hi))
  67. (let ((c (if (or (<= 0 i #xd7ff)
  68. (<= #xe000 i #x10ffff))
  69. (integer->char i)
  70. replacement-character)))
  71. (string-set! s (- i lo) c)))
  72. (check (string=? (tostring (tobytevector s)) s) => #t)))
  73. (define (test-exhaustively name tostring tobytevector)
  74. (test-char-range 0 #xffff tostring tobytevector)
  75. (test-char-range #x10000 #x1ffff tostring tobytevector)
  76. (test-char-range #x20000 #x2ffff tostring tobytevector)
  77. (test-char-range #x30000 #x3ffff tostring tobytevector)
  78. (test-char-range #x40000 #x4ffff tostring tobytevector)
  79. (test-char-range #x50000 #x5ffff tostring tobytevector)
  80. (test-char-range #x60000 #x6ffff tostring tobytevector)
  81. (test-char-range #x70000 #x7ffff tostring tobytevector)
  82. (test-char-range #x80000 #x8ffff tostring tobytevector)
  83. (test-char-range #x90000 #x9ffff tostring tobytevector)
  84. (test-char-range #xa0000 #xaffff tostring tobytevector)
  85. (test-char-range #xb0000 #xbffff tostring tobytevector)
  86. (test-char-range #xc0000 #xcffff tostring tobytevector)
  87. (test-char-range #xd0000 #xdffff tostring tobytevector)
  88. (test-char-range #xe0000 #xeffff tostring tobytevector)
  89. (test-char-range #xf0000 #xfffff tostring tobytevector)
  90. (test-char-range #x100000 #x10ffff tostring tobytevector))
  91. ; Feel free to replace this with your favorite timing macro.
  92. (define-test-case utf-8 string-bytevectors-tests
  93. (check-that (string->utf8 "k\x007f;\x0080;\x07ff;\x0800;\xffff;")
  94. (is bytevector=?
  95. (u8-list->bytevector '(#x6b
  96. #x7f
  97. #b11000010 #b10000000
  98. #b11011111 #b10111111
  99. #b11100000 #b10100000 #b10000000
  100. #b11101111 #b10111111 #b10111111))))
  101. (check-that (u8-list->bytevector '(#b11110000 #b10010000 #b10000000 #b10000000
  102. #b11110100 #b10001111 #b10111111 #b10111111))
  103. (is bytevector=?
  104. (string->utf8 "\x010000;\x10ffff;")))
  105. (check (utf8->string (u8-list->bytevector '(#x61 ; a
  106. #xc0 #x62 ; ?b
  107. #xc1 #x63 ; ?c
  108. #xc2 #x64 ; ?d
  109. #x80 #x65 ; ?e
  110. #xc0 #xc0 #x66 ; ??f
  111. #xe0 #x67 ; ?g
  112. )))
  113. => "a\xfffd;b\xfffd;c\xfffd;d\xfffd;e\xfffd;\xfffd;f\xfffd;g")
  114. (check (utf8->string (u8-list->bytevector '(#x20 #x68 ; ???h
  115. #xe0 #xc0 #x20 #x69 ; ???i
  116. #xf0 #x6a ; ?j
  117. )))
  118. => " h\xfffd;\xfffd; i\xfffd;j")
  119. (check (utf8->string (u8-list->bytevector '(#x61 ; a
  120. #x20 #x20 #x20 #x62 ; ????b
  121. #xf0 #xFF #x63 ; .c
  122. )))
  123. => "a\x20;\x20;\x20;b\xfffd;\xfffd;c")
  124. (check (utf8->string (u8-list->bytevector '(#x61 ; a
  125. #x80 #xc6 #x64 ; ??d
  126. #x80 #xc6 #x65 ; ??e
  127. #x80 #xc4 #x66 ; ??f
  128. )))
  129. => "a\xfffd;\xfffd;d\xfffd;\xfffd;e\xfffd;\xfffd;f")
  130. (check (utf8->string (u8-list->bytevector '(#x61 ; a
  131. #xc6 #x80 #x64 ; .d
  132. #xc6 #x80 #x65 ; ?e
  133. #xc4 #x80 #x66 ; ?f
  134. )))
  135. => "a\x00180;d\x00180;e\x00100;f")
  136. (check (utf8->string (u8-list->bytevector '(#x61 ; a
  137. #xf4 #x8f #xbf #xbf #x62 ; .b
  138. #xf4 #x90 #x80 #x80 #x63 ; ????c
  139. )))
  140. => "a\x10ffff;b\xfffd;\xfffd;\xfffd;\xfffd;c")
  141. (check (utf8->string (u8-list->bytevector '(#x61 ; a
  142. #xf5 #x80 #x80 #x80 #x64 ; ????d
  143. )))
  144. => "a\xfffd;\xfffd;\xfffd;\xfffd;d")
  145. ;; ignores BOM signature
  146. (check (utf8->string (u8-list->bytevector '(#xef #xbb #xbf #x61 #x62 #x63 #x64)))
  147. => "abcd")
  148. (test-roundtrip (random-bytevector 10) utf8->string string->utf8)
  149. (do ((i 0 (+ i 1)))
  150. ((= i *random-stress-tests*))
  151. (test-roundtrip (random-bytevector *random-stress-test-max-size*)
  152. utf8->string string->utf8)))
  153. (define-test-case utf-16 string-bytevectors-tests
  154. (check-that (string->utf16 "k\x007f;\x0080;\x07ff;\x0800;\xffff;")
  155. (is bytevector=?
  156. (u8-list->bytevector '(#x00 #x6b
  157. #x00 #x7f
  158. #x00 #x80
  159. #x07 #xff
  160. #x08 #x00
  161. #xff #xff))))
  162. (check-that (string->utf16 "k\x007f;\x0080;\x07ff;\x0800;\xffff;"
  163. (endianness little))
  164. (is bytevector=?
  165. (u8-list->bytevector '(#x6b #x00
  166. #x7f #x00
  167. #x80 #x00
  168. #xff #x07
  169. #x00 #x08
  170. #xff #xff))))
  171. (check-that (string->utf16 "\x010000;\xfdcba;\x10ffff;")
  172. (is bytevector=?
  173. (u8-list->bytevector '(#xd8 #x00 #xdc #x00
  174. #xdb #xb7 #xdc #xba
  175. #xdb #xff #xdf #xff))))
  176. (check-that (string->utf16 "\x010000;\xfdcba;\x10ffff;" (endianness little))
  177. (is bytevector=?
  178. (u8-list->bytevector '(#x00 #xd8 #x00 #xdc
  179. #xb7 #xdb #xba #xdc
  180. #xff #xdb #xff #xdf))))
  181. (check-that (string->utf16 "ab\x010000;\xfdcba;\x10ffff;cd")
  182. (is bytevector=?
  183. (string->utf16 "ab\x010000;\xfdcba;\x10ffff;cd" (endianness big))))
  184. (check (utf16->string
  185. (u8-list->bytevector '(#x00 #x6b
  186. #x00 #x7f
  187. #x00 #x80
  188. #x07 #xff
  189. #x08 #x00
  190. #xff #xff))
  191. (endianness big))
  192. => "k\x007f;\x0080;\x07ff;\x0800;\xffff;")
  193. (check (utf16->string
  194. (u8-list->bytevector '(#x00 #x6b
  195. #x00 #x7f
  196. #x00 #x80
  197. #x07 #xff
  198. #x08 #x00
  199. #xff #xff))
  200. (endianness big))
  201. => "k\x007f;\x0080;\x07ff;\x0800;\xffff;")
  202. (check (utf16->string
  203. (u8-list->bytevector '(#xfe #xff ; big-endian BOM
  204. #x00 #x6b
  205. #x00 #x7f
  206. #x00 #x80
  207. #x07 #xff
  208. #x08 #x00
  209. #xff #xff))
  210. (endianness big))
  211. => "k\x007f;\x0080;\x07ff;\x0800;\xffff;")
  212. (check (utf16->string
  213. (u8-list->bytevector '(#x6b #x00
  214. #x7f #x00
  215. #x80 #x00
  216. #xff #x07
  217. #x00 #x08
  218. #xff #xff))
  219. (endianness little))
  220. => "k\x007f;\x0080;\x07ff;\x0800;\xffff;")
  221. (check (utf16->string
  222. (u8-list->bytevector '(#xff #xfe ; little-endian BOM
  223. #x6b #x00
  224. #x7f #x00
  225. #x80 #x00
  226. #xff #x07
  227. #x00 #x08
  228. #xff #xff))
  229. (endianness big))
  230. => "k\x007f;\x0080;\x07ff;\x0800;\xffff;")
  231. (let ((tostring (lambda (bv) (utf16->string bv (endianness big))))
  232. (tostring-big (lambda (bv) (utf16->string bv (endianness big))))
  233. (tostring-little (lambda (bv) (utf16->string bv (endianness little))))
  234. (tobvec string->utf16)
  235. (tobvec-big (lambda (s) (string->utf16 s (endianness big))))
  236. (tobvec-little (lambda (s) (string->utf16 s (endianness little)))))
  237. (do ((i 0 (+ i 1)))
  238. ((= i *random-stress-tests*))
  239. (test-roundtrip (random-bytevector2 *random-stress-test-max-size*)
  240. tostring tobvec)
  241. (test-roundtrip (random-bytevector2 *random-stress-test-max-size*)
  242. tostring-big tobvec-big)
  243. (test-roundtrip (random-bytevector2 *random-stress-test-max-size*)
  244. tostring-little tobvec-little))))
  245. (define-test-case utf-32 string-bytevectors-tests
  246. (check-that (string->utf32 "abc")
  247. (is bytevector=?
  248. (u8-list->bytevector '(#x00 #x00 #x00 #x61
  249. #x00 #x00 #x00 #x62
  250. #x00 #x00 #x00 #x63))))
  251. (check-that (string->utf32 "abc" (endianness big))
  252. (is bytevector=?
  253. (u8-list->bytevector '(#x00 #x00 #x00 #x61
  254. #x00 #x00 #x00 #x62
  255. #x00 #x00 #x00 #x63))))
  256. (check-that (string->utf32 "abc" (endianness little))
  257. (is bytevector=?
  258. (u8-list->bytevector '(#x61 #x00 #x00 #x00
  259. #x62 #x00 #x00 #x00
  260. #x63 #x00 #x00 #x00))))
  261. (check (string-contains ;;"a\xfffd;\xfffd;\xfffd;\xfffd;b\xfffd;\xfffd;\xfffd;c\xfffd;d\xfffd;\xfffd;\xfffd;\xfffd;e"
  262. (utf32->string
  263. (u8-list->bytevector '(#x00 #x00 #x00 #x61
  264. #x00 #x00 #xd9 #x00
  265. #x00 #x00 #x00 #x62
  266. #x00 #x00 #xdd #xab
  267. #x00 #x00 #x00 #x63
  268. #x00 #x11 #x00 #x00
  269. #x00 #x00 #x00 #x64
  270. #x01 #x00 #x00 #x65
  271. #x00 #x00 #x00 #x65))
  272. (endianness big))
  273. "a\xfffd;"))
  274. (check (string-contains ;;"a\xfffd;b\xfffd;c\xfffd;d\xfffd;e"
  275. (utf32->string
  276. (u8-list->bytevector '(#x00 #x00 #x00 #x61
  277. #x00 #x00 #xd9 #x00
  278. #x00 #x00 #x00 #x62
  279. #x00 #x00 #xdd #xab
  280. #x00 #x00 #x00 #x63
  281. #x00 #x11 #x00 #x00
  282. #x00 #x00 #x00 #x64
  283. #x01 #x00 #x00 #x65
  284. #x00 #x00 #x00 #x65))
  285. (endianness big))
  286. "a\xfffd;"))
  287. (check (string-contains ;;"a\xfffd;b\xfffd;c\xfffd;d\xfffd;e"
  288. (utf32->string
  289. (u8-list->bytevector '(#x00 #x00 #xfe #xff ; big-endian BOM
  290. #x00 #x00 #x00 #x61
  291. #x00 #x00 #xd9 #x00
  292. #x00 #x00 #x00 #x62
  293. #x00 #x00 #xdd #xab
  294. #x00 #x00 #x00 #x63
  295. #x00 #x11 #x00 #x00
  296. #x00 #x00 #x00 #x64
  297. #x01 #x00 #x00 #x65
  298. #x00 #x00 #x00 #x65))
  299. (endianness big))
  300. "a\xfffd;"))
  301. (check (string-contains ;;"\xfeff;a\xfffd;b\xfffd;c\xfffd;d\xfffd;e"
  302. (utf32->string
  303. (u8-list->bytevector '(#x00 #x00 #xfe #xff ; big-endian BOM
  304. #x00 #x00 #x00 #x61
  305. #x00 #x00 #xd9 #x00
  306. #x00 #x00 #x00 #x62
  307. #x00 #x00 #xdd #xab
  308. #x00 #x00 #x00 #x63
  309. #x00 #x11 #x00 #x00
  310. #x00 #x00 #x00 #x64
  311. #x01 #x00 #x00 #x65
  312. #x00 #x00 #x00 #x65))
  313. (endianness big))
  314. "\xfffd;"))
  315. (check (string-contains ;;"a\xfffd;b\xfffd;c\xfffd;d\xfffd;e"
  316. (utf32->string
  317. (u8-list->bytevector '(#x61 #x00 #x00 #x00
  318. #x00 #xd9 #x00 #x00
  319. #x62 #x00 #x00 #x00
  320. #xab #xdd #x00 #x00
  321. #x63 #x00 #x00 #x00
  322. #x00 #x00 #x11 #x00
  323. #x64 #x00 #x00 #x00
  324. #x65 #x00 #x00 #x01
  325. #x65 #x00 #x00 #x00))
  326. (endianness little))
  327. "a\xfffd;"))
  328. (check (string-contains ;;"a\xfffd;b\xfffd;c\xfffd;d\xfffd;e"
  329. (utf32->string
  330. (u8-list->bytevector '(#xff #xfe #x00 #x00 ; little-endian BOM
  331. #x61 #x00 #x00 #x00
  332. #x00 #xd9 #x00 #x00
  333. #x62 #x00 #x00 #x00
  334. #xab #xdd #x00 #x00
  335. #x63 #x00 #x00 #x00
  336. #x00 #x00 #x11 #x00
  337. #x64 #x00 #x00 #x00
  338. #x65 #x00 #x00 #x01
  339. #x65 #x00 #x00 #x00))
  340. (endianness big))
  341. "a\xfffd;"))
  342. (check (string-contains ;;"a\xfffd;b\xfffd;c\xfffd;d\xfffd;e"
  343. (utf32->string
  344. (u8-list->bytevector '(#xff #xfe #x00 #x00 ; little-endian BOM
  345. #x61 #x00 #x00 #x00
  346. #x00 #xd9 #x00 #x00
  347. #x62 #x00 #x00 #x00
  348. #xab #xdd #x00 #x00
  349. #x63 #x00 #x00 #x00
  350. #x00 #x00 #x11 #x00
  351. #x64 #x00 #x00 #x00
  352. #x65 #x00 #x00 #x01
  353. #x65 #x00 #x00 #x00))
  354. (endianness little))
  355. "a\xfffd;"))
  356. (let ((tostring (lambda (bv) (utf32->string bv (endianness big))))
  357. (tostring-big (lambda (bv) (utf32->string bv (endianness big))))
  358. (tostring-little (lambda (bv) (utf32->string bv (endianness little))))
  359. (tobvec string->utf32)
  360. (tobvec-big (lambda (s) (string->utf32 s (endianness big))))
  361. (tobvec-little (lambda (s) (string->utf32 s (endianness little)))))
  362. (do ((i 0 (+ i 1)))
  363. ((= i *random-stress-tests*))
  364. (test-roundtrip (random-bytevector4 *random-stress-test-max-size*)
  365. tostring tobvec)
  366. (test-roundtrip (random-bytevector4 *random-stress-test-max-size*)
  367. tostring-big tobvec-big)
  368. (test-roundtrip (random-bytevector4 *random-stress-test-max-size*)
  369. tostring-little tobvec-little))))
  370. ; Tests string <-> bytevector conversion on strings
  371. ; that contain every Unicode scalar value.
  372. (define-test-case exhaustive-string-bytevector-tests string-bytevectors-tests
  373. ;; Tests throughout an inclusive range.
  374. (test-exhaustively "UTF-16BE"
  375. (lambda (bv) (utf16->string bv (endianness big)))
  376. (lambda (s) (string->utf16 s (endianness big))))
  377. (test-exhaustively "UTF-16LE"
  378. (lambda (bv) (utf16->string bv (endianness little)))
  379. (lambda (s) (string->utf16 s (endianness little))))
  380. (test-exhaustively "UTF-32"
  381. (lambda (bv) (utf32->string bv (endianness big)))
  382. string->utf32)
  383. (test-exhaustively "UTF-32BE"
  384. (lambda (bv) (utf32->string bv (endianness big)))
  385. (lambda (s) (string->utf32 s (endianness big))))
  386. (test-exhaustively "UTF-32LE"
  387. (lambda (bv) (utf32->string bv (endianness little)))
  388. (lambda (s) (string->utf32 s (endianness little)))))