summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Montague <mikemon@gmail.com>2022-07-31 14:58:13 -0700
committerMike Montague <mikemon@gmail.com>2022-07-31 14:58:13 -0700
commit54fd0217f83c91276600625b53882cd0472e80a1 (patch)
treed397745db901ba06f43a96287c39f6fe442678e7
parent277d9a4b7d8ca3e8d3b567f25e82dd5a6cfcb02a (diff)
srfi 207: constructors and conversion
-rw-r--r--src/base.scm8
-rw-r--r--src/foment.hpp4
-rw-r--r--src/read.cpp81
-rw-r--r--src/srfi-166.scm4
-rw-r--r--src/vectors.cpp350
-rw-r--r--test/srfi.scm361
-rw-r--r--unix/makefile6
-rw-r--r--windows/makefile6
8 files changed, 791 insertions, 29 deletions
diff --git a/src/base.scm b/src/base.scm
index 85b6db0..ef9c6fa 100644
--- a/src/base.scm
+++ b/src/base.scm
@@ -696,7 +696,13 @@
set-procedure-property!
%procedure->formatter
%formatter?
- call-with-parameterize)
+ call-with-parameterize
+ make-bytestring
+ bytevector->hex-string
+ hex-string->bytevector
+ bytevector->base64
+ base64->bytevector
+ bytestring->list)
(cond-expand
(unix
(export
diff --git a/src/foment.hpp b/src/foment.hpp
index ae417b2..994341c 100644
--- a/src/foment.hpp
+++ b/src/foment.hpp
@@ -8,8 +8,9 @@ To Do:
-- Bytevectors: R6RS (rnrs bytevectors) as (scheme bytevector) (note singular form)
-- SRFI 129: Titlecase Procedures
-- SRFI 154: First-class dynamic extents
+-- SRFI 158: Generators and Accumulators
-- SRFI 195: Multiple-value boxes
--- SRIF 207: String-notated bytevectors
+-- SRFI 207: String-notated bytevectors
-- IO: FAlive, EnterWait, and LeaveWait
-- Allow GC on nested executions
@@ -1133,6 +1134,7 @@ extern FObject StartThreadOutOfMemory;
extern FObject ExecuteStackOverflow;
extern FObject FileErrorSymbol;
extern FObject PositionErrorSymbol;
+extern FObject BytestringErrorSymbol;
extern FObject NoValuePrimitive;
// ---- Flonums ----
diff --git a/src/read.cpp b/src/read.cpp
index fe0392c..f7232d3 100644
--- a/src/read.cpp
+++ b/src/read.cpp
@@ -25,6 +25,8 @@ Foment
#define MAXIMUM_NUMBER 256
#define MAXIMUM_NAME 32
+EternalSymbol(BytestringErrorSymbol, "bytestring-error");
+
// ---- Datum Reference ----
#define AsDatumReference(obj) ((FDatumReference *) (obj))
@@ -86,18 +88,18 @@ static long_t DotSubsequentP(FCh ch)
#define DotObject ((FObject) -1)
#define EolObject ((FObject *) -2)
-static FCh ReadStringHexChar(FObject port)
+static FCh ReadStringHexChar(FObject port, int bsf)
{
FAlive ap(&port);
FCh s[16];
long_t sl = 2;
FCh ch;
+ FObject n;
for (;;)
{
if (ReadCh(port, &ch) == 0)
- RaiseExceptionC(Lexical, "read", "unexpected end-of-file reading string",
- List(port));
+ goto MissingSemicolon;
if (ch == ';')
break;
@@ -105,25 +107,34 @@ static FCh ReadStringHexChar(FObject port)
s[sl] = ch;
sl += 1;
if (sl == sizeof(s) / sizeof(FCh))
- RaiseExceptionC(Lexical, "read",
- "missing ; to terminate \\x<hex-value> in string", List(port));
+ goto MissingSemicolon;
}
- FObject n = StringToNumber(s + 2, sl - 2, 16);
+ n = StringToNumber(s + 2, sl - 2, 16);
if (FixnumP(n) == 0)
{
s[0] = '\\';
s[1] = 'x';
- RaiseExceptionC(Lexical, "read", "expected a valid hexidecimal value for a character",
+ RaiseExceptionC(Lexical, "read", bsf ? BytestringErrorSymbol : NoValueObject,
+ "expected a valid hexidecimal value for a character",
List(port, MakeString(s, sl)));
}
return((FCh) AsFixnum(n));
+
+MissingSemicolon:
+ if (bsf)
+ RaiseExceptionC(Lexical, "read", BytestringErrorSymbol,
+ "missing ; to terminate \\x<hex-value> in bytestring", List(port));
+ else
+ RaiseExceptionC(Lexical, "read",
+ "missing ; to terminate \\x<hex-value> in string", List(port));
+ return(0);
}
-static FObject ReadStringLiteral(FObject port, FCh tch)
+static FObject ReadStringLiteral(FObject port, FCh tch, int bsf)
{
FAlive ap(&port);
FCh sb[128];
@@ -184,11 +195,16 @@ Again:
case '\\': ch = 0x005C; break;
case '|': ch = 0x007C; break;
case 'x':
- ch = ReadStringHexChar(port);
+ ch = ReadStringHexChar(port, bsf);
break;
default:
- RaiseExceptionC(Lexical, "read", "unexpected character following \\",
- List(port, MakeCharacter(ch)));
+ if (bsf)
+ RaiseExceptionC(Lexical, "read", BytestringErrorSymbol,
+ "unexpected character following \\ in bytestring",
+ List(port, MakeCharacter(ch)));
+ else
+ RaiseExceptionC(Lexical, "read", "unexpected character following \\ in string",
+ List(port, MakeCharacter(ch)));
}
}
@@ -201,7 +217,8 @@ Again:
{
if (s != sb)
free(s);
- RaiseExceptionC(Restriction, "read", "string too long", List(port));
+ RaiseExceptionC(Restriction, "read", bsf ? "bytestring too long" :
+ "string too long", List(port));
}
memcpy(ns, s, msl * sizeof(FCh));
@@ -220,10 +237,28 @@ Again:
UnexpectedEof:
if (s != sb)
free(s);
- RaiseExceptionC(Lexical, "read", "unexpected end-of-file reading string", List(port));
+ RaiseExceptionC(Lexical, "read", bsf ? "unexpected end-of-file reading bytestring" :
+ "unexpected end-of-file reading string", List(port));
return(NoValueObject);
}
+static FObject ReadBytestring(FObject port)
+{
+ FObject s = ReadStringLiteral(port, '"', 1);
+ long_t sl = StringLength(s);
+ FObject bv = MakeBytevector(sl);
+ for (long_t idx = 0; idx < sl; idx += 1)
+ {
+ FCh ch = AsString(s)->String[idx];
+ if (ch >= 128 )
+ RaiseExceptionC(Lexical, "read", BytestringErrorSymbol,
+ "unexpected character in bytestring", List(MakeCharacter(ch)));
+ AsBytevector(bv)->Vector[idx] = (FByte) ch;
+ }
+
+ return(bv);
+}
+
static FObject ReadNumber(FObject port, FCh * s, long_t sdx, long_t rdx, long_t df)
{
FAlive ap(&port);
@@ -448,14 +483,16 @@ static FObject ReadSharp(FObject port, long_t eaf, long_t rlf, FObject * pdlhtbl
RaiseExceptionC(Lexical, "read", "unexpected end-of-file reading bytevector",
List(port));
if (ch != '8')
- RaiseExceptionC(Lexical, "read", "expected #\\u8(", List(port));
+ RaiseExceptionC(Lexical, "read", "expected #u8(", List(port));
if (ReadCh(port, &ch) == 0)
RaiseExceptionC(Lexical, "read", "unexpected end-of-file reading bytevector",
List(port));
- if (ch != '(')
- RaiseExceptionC(Lexical, "read", "expected #\\u8(", List(port));
- return(U8ListToBytevector(ReadList(port, pdlhtbl)));
+ if (ch == '(')
+ return(U8ListToBytevector(ReadList(port, pdlhtbl)));
+ else if (ch == '"')
+ return(ReadBytestring(port));
+ RaiseExceptionC(Lexical, "read", "expected #u8( or #u8\"", List(port));
}
else if (ch == ';')
{
@@ -574,7 +611,7 @@ static FObject Read(FObject port, long_t eaf, long_t rlf, FObject * pdlhtbl)
return(ReadSharp(port, eaf, rlf, pdlhtbl));
case '"':
- return(ReadStringLiteral(port, '"'));
+ return(ReadStringLiteral(port, '"', 0));
case '|':
{
@@ -584,8 +621,8 @@ static FObject Read(FObject port, long_t eaf, long_t rlf, FObject * pdlhtbl)
ln = GetLineColumn(port, 0);
FObject sym = FoldcasePortP(port)
- ? StringToSymbol(FoldcaseString(ReadStringLiteral(port, '|')))
- : StringToSymbol(ReadStringLiteral(port, '|'));
+ ? StringToSymbol(FoldcaseString(ReadStringLiteral(port, '|', 0)))
+ : StringToSymbol(ReadStringLiteral(port, '|', 0));
return(WantIdentifiersPortP(port) ? MakeIdentifier(sym, GetFilename(port), ln)
: sym);
}
@@ -1024,6 +1061,10 @@ void SetupRead()
{
FAssert(MAXIMUM_NUMBER == MAXIMUM_IDENTIFIER);
+ BytestringErrorSymbol = InternSymbol(BytestringErrorSymbol);
+
+ FAssert(BytestringErrorSymbol = StringCToSymbol("bytestring-error"));
+
for (ulong_t idx = 0; idx < sizeof(Primitives) / sizeof(FPrimitive *); idx++)
DefinePrimitive(Bedrock, BedrockLibrary, Primitives[idx]);
}
diff --git a/src/srfi-166.scm b/src/srfi-166.scm
index b30388f..006dbde 100644
--- a/src/srfi-166.scm
+++ b/src/srfi-166.scm
@@ -1311,8 +1311,8 @@
sign-rule
comma-rule
comma-sep
- word-separator?
- ))
+ word-separator?)
+ )
(define-library (srfi 166 pretty)
(import (srfi 166))
diff --git a/src/vectors.cpp b/src/vectors.cpp
index 069d9dc..eb4764c 100644
--- a/src/vectors.cpp
+++ b/src/vectors.cpp
@@ -4,6 +4,7 @@ Foment
*/
+#include <stdio.h>
#include <string.h>
#include "foment.hpp"
#include "unicode.hpp"
@@ -882,6 +883,347 @@ Define("string->utf8", StringToUtf8Primitive)(long_t argc, FObject argv[])
return(ConvertStringToUtf8(AsString(argv[0])->String + strt, end - strt, 0));
}
+// ---- SRFI 207: String-notated bytevectors ----
+
+Define("make-bytestring", MakeBytestringPrimitive)(long_t argc, FObject argv[])
+{
+ OneArgCheck("make-bytestring", argc);
+
+ FObject lst = argv[0];
+ long_t bsl = 0;
+
+ while (PairP(lst))
+ {
+ FObject arg = First(lst);
+ if (FixnumP(arg))
+ {
+ if (AsFixnum(arg) < 0 || AsFixnum(arg) > 255)
+ RaiseExceptionC(Assertion, "make-bytestring", BytestringErrorSymbol,
+ "expected an integer between 0 and 155", List(arg));
+
+ bsl += 1;
+ }
+ else if (CharacterP(arg))
+ {
+ if (AsCharacter(arg) >= 128)
+ RaiseExceptionC(Assertion, "make-bytestring", BytestringErrorSymbol,
+ "expected an ascii character", List(arg));
+
+ bsl += 1;
+ }
+ else if (BytevectorP(arg))
+ bsl += BytevectorLength(arg);
+ else if (StringP(arg))
+ bsl += StringLength(arg);
+ else
+ RaiseExceptionC(Assertion, "make-bytestring", BytestringErrorSymbol,
+ "expected a character, integer, bytevector, or a string", List(arg));
+
+ lst = Rest(lst);
+ }
+
+ if (lst != EmptyListObject)
+ RaiseExceptionC(Assertion, "make-bytestring", "expected a valid list", List(argv[0]));
+
+ FObject bv = MakeBytevector(bsl);
+ FByte * bs = AsBytevector(bv)->Vector;
+ long_t bdx = 0;
+
+ lst = argv[0];
+ while (PairP(lst))
+ {
+ FObject arg = First(lst);
+ if (FixnumP(arg))
+ {
+ bs[bdx] = (FByte) AsFixnum(arg);
+ bdx += 1;
+ }
+ else if (CharacterP(arg))
+ {
+ bs[bdx] = (FByte) AsCharacter(arg);
+ bdx += 1;
+ }
+ else if (BytevectorP(arg))
+ {
+ memcpy(bs + bdx, AsBytevector(arg)->Vector, BytevectorLength(arg));
+ bdx += BytevectorLength(arg);
+ }
+ else if (StringP(arg))
+ {
+ long_t sl = StringLength(arg);
+ for (long_t sdx = 0; sdx < sl; sdx += 1)
+ {
+ FCh ch = AsString(arg)->String[sdx];
+ if (ch >= 128)
+ RaiseExceptionC(Assertion, "make-bytestring", BytestringErrorSymbol,
+ "expected a string of ascii characters", List(arg));
+ bs[bdx + sdx] = (FByte) ch;
+ }
+
+ bdx += sl;
+ }
+ else
+ {
+ FAssert(0);
+ }
+
+ lst = Rest(lst);
+ }
+
+ return(bv);
+}
+
+const char * hexdigit = "0123456789abcdef";
+
+Define("bytevector->hex-string", BytevectorToHexStringPrimitive)(long_t argc, FObject argv[])
+{
+ OneArgCheck("bytevector->hex-string", argc);
+ BytevectorArgCheck("bytevector->hex-string", argv[0]);
+
+ ulong_t vl = BytevectorLength(argv[0]);
+ FObject ret = MakeStringCh(vl * 2, 0);
+ FCh * s = AsString(ret)->String;
+ FByte * v = AsBytevector(argv[0])->Vector;
+
+ for (ulong_t vdx = 0; vdx < vl; vdx += 1)
+ {
+ s[vdx * 2] = hexdigit[(v[vdx] >> 4) & 0x0F];
+ s[vdx * 2 + 1] = hexdigit[v[vdx] & 0x0F];
+ }
+
+ return(ret);
+}
+
+static FByte ConvertHexDigit(FCh ch)
+{
+ if (ch >= '0' && ch <= '9')
+ return(ch - '0');
+ else if (ch >= 'a' && ch <= 'f')
+ return(ch - 'a' + 10);
+ else if (ch >= 'A' && ch <= 'F')
+ return(ch - 'A' + 10);
+
+ RaiseExceptionC(Assertion, "hex-string->bytevector", BytestringErrorSymbol,
+ "expected a hexidecimal digit", List(MakeCharacter(ch)));
+ return(0);
+}
+
+Define("hex-string->bytevector", HexStringToBytevectorPrimitive)(long_t argc, FObject argv[])
+{
+ OneArgCheck("hex-string->bytevector", argc);
+ StringArgCheck("hex-string->bytevector", argv[0]);
+
+ ulong_t sl = StringLength(argv[0]);
+
+ if (sl % 2 != 0)
+ RaiseExceptionC(Assertion, "hex-string->bytevector", BytestringErrorSymbol,
+ "expected a string of pairs of hexidecimal digits", List(argv[0]));
+
+ FObject ret = MakeBytevector(sl / 2, "hex-string->bytevector");
+ FCh * s = AsString(argv[0])->String;
+ FByte * v = AsBytevector(ret)->Vector;
+
+ for (ulong_t sdx = 0; sdx < sl; sdx += 2)
+ v[sdx / 2] = (ConvertHexDigit(s[sdx]) << 4) | ConvertHexDigit(s[sdx + 1]) ;
+
+ return(ret);
+}
+
+const char * base64digit = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+static FCh Base64Digit(ulong_t d, FCh digits[2])
+{
+ FAssert(d < 64);
+
+ if (d > 61)
+ return(digits[d - 62]);
+ return(base64digit[d]);
+}
+
+Define("bytevector->base64", BytevectorToBase64Primitive)(long_t argc, FObject argv[])
+{
+ OneOrTwoArgsCheck("bytevector->base64", argc);
+ BytevectorArgCheck("bytevector->base64", argv[0]);
+
+ FCh digits[2] = {'+', '/'};
+
+ if (argc == 2)
+ {
+ if (StringP(argv[1]) == 0 || StringLength(argv[1]) != 2)
+ RaiseExceptionC(Assertion, "bytevector->base64", "expected a two character string",
+ List(argv[1]));
+
+ digits[0] = AsString(argv[1])->String[0];
+ digits[1] = AsString(argv[1])->String[1];
+ }
+
+ ulong_t vl = BytevectorLength(argv[0]);
+ FObject ret = MakeStringCh((vl / 3) * 4 + (vl % 3 == 0 ? 0 : 4), 0);
+ FCh * s = AsString(ret)->String;
+ FByte * v = AsBytevector(argv[0])->Vector;
+
+ ulong_t sdx = 0;
+ for (ulong_t vdx = 3; vdx <= vl; vdx += 3)
+ {
+ ulong_t d = (v[vdx - 3] << 16) | (v[vdx - 2] << 8) | v[vdx - 1];
+ s[sdx] = Base64Digit((d >> 18) & 0x3F, digits);
+ s[sdx + 1] = Base64Digit((d >> 12) & 0x3F, digits);
+ s[sdx + 2] = Base64Digit((d >> 6) & 0x3F, digits);
+ s[sdx + 3] = Base64Digit(d & 0x3F, digits);
+ sdx += 4;
+ }
+
+ if (vl % 3 == 1)
+ {
+ ulong_t d = v[vl - 1] << 16;
+ s[sdx] = Base64Digit((d >> 18) & 0x3F, digits);
+ s[sdx + 1] = Base64Digit((d >> 12) & 0x3F, digits);
+ s[sdx + 2] = '=';
+ s[sdx + 3] = '=';
+ } else if (vl % 3 == 2)
+ {
+ ulong_t d = (v[vl - 2] << 16) | (v[vl - 1] << 8);
+ s[sdx] = Base64Digit((d >> 18) & 0x3F, digits);
+ s[sdx + 1] = Base64Digit((d >> 12) & 0x3F, digits);
+ s[sdx + 2] = Base64Digit((d >> 6) & 0x3F, digits);
+ s[sdx + 3] = '=';
+ }
+
+ return(ret);
+}
+
+static ulong_t SkipWhitespace(FCh * s, ulong_t sl, ulong_t sdx)
+{
+ while (sdx < sl && WhitespaceP(s[sdx]))
+ sdx += 1;
+
+ return(sdx);
+}
+
+static ulong_t ConvertBase64(FCh ch, FCh digits[2])
+{
+ if (ch == digits[0])
+ return(62);
+ else if (ch == digits[1])
+ return(63);
+ else if (ch >= 'A' && ch <= 'Z')
+ return(ch - 'A');
+ else if (ch >= 'a' && ch <= 'z')
+ return(ch - 'a' + 26);
+ else if (ch >= '0' && ch <= '9')
+ return(ch - '0' + 52);
+
+ RaiseExceptionC(Assertion, "base64->bytevector", BytestringErrorSymbol,
+ "expected a base64 digit", List(MakeCharacter(ch)));
+ return(0);
+}
+
+Define("base64->bytevector", Base64ToBytevectorPrimitive)(long_t argc, FObject argv[])
+{
+ OneOrTwoArgsCheck("base64->bytevector", argc);
+ StringArgCheck("base64->bytevector", argv[0]);
+
+ FCh digits[2] = {'+', '/'};
+
+ if (argc == 2)
+ {
+ if (StringP(argv[1]) == 0 || StringLength(argv[1]) != 2)
+ RaiseExceptionC(Assertion, "base64->bytevector", "expected a two character string",
+ List(argv[1]));
+
+ digits[0] = AsString(argv[1])->String[0];
+ digits[1] = AsString(argv[1])->String[1];
+ }
+
+ ulong_t sl = StringLength(argv[0]);
+ FCh * s = AsString(argv[0])->String;
+ ulong_t sdx = 0;
+ FObject lst = EmptyListObject;
+ for (;;)
+ {
+ ulong_t d = 0;
+ long_t eq = 0;
+ for (long_t cnt = 0; cnt < 4; cnt += 1)
+ {
+ sdx = SkipWhitespace(s, sl, sdx);
+ if (sdx == sl)
+ {
+ if (cnt > 0)
+ goto Failed;
+
+ return(U8ListToBytevector(ReverseListModify(lst)));
+ }
+
+ if (eq > 0 && s[sdx] != '=')
+ goto Failed;
+
+ if (cnt > 1 && s[sdx] == '=')
+ {
+ d = d << 6;
+ eq += 1;
+ }
+ else
+ d = (d << 6) | ConvertBase64(s[sdx], digits);
+ sdx += 1;
+ }
+
+ if (eq > 2)
+ goto Failed;
+ else if (eq > 0)
+ {
+ sdx = SkipWhitespace(s, sl, sdx);
+ if (sdx < sl)
+ goto Failed;
+ }
+
+ lst = MakePair(MakeFixnum((d >> 16) & 0xFF), lst);
+ if (eq < 2)
+ lst = MakePair(MakeFixnum((d >> 8) & 0xFF), lst);
+ if (eq < 1)
+ lst = MakePair(MakeFixnum(d & 0xFF), lst);
+ }
+
+Failed:
+ RaiseExceptionC(Assertion, "base64->bytevector", BytestringErrorSymbol,
+ "expected a base64 string", List(argv[0]));
+ return(NoValueObject);
+}
+
+Define("bytestring->list", BytestringToListPrimitive)(long_t argc, FObject argv[])
+{
+ OneToThreeArgsCheck("bytestring->list", argc);
+ BytevectorArgCheck("bytestring->list", argv[0]);
+
+ long_t vdx = 0;
+ long_t end = BytevectorLength(argv[0]);
+ if (argc > 1)
+ {
+ IndexArgCheck("bytestring->list", argv[1], end);
+
+ vdx = AsFixnum(argv[1]);
+ }
+ if (argc > 2)
+ {
+ EndIndexArgCheck("bytestring->list", argv[1], vdx, end);
+
+ end = AsFixnum(argv[2]);
+ }
+
+ FByte * v = AsBytevector(argv[0])->Vector;
+ FObject lst = EmptyListObject;
+ while (vdx < end)
+ {
+ if (v[vdx] >= 32 && v[vdx] <= 127)
+ lst = MakePair(MakeCharacter(v[vdx]), lst);
+ else
+ lst = MakePair(MakeFixnum(v[vdx]), lst);
+
+ vdx += 1;
+ }
+
+ return(ReverseListModify(lst));
+}
+
static FObject Primitives[] =
{
VectorPPrimitive,
@@ -913,7 +1255,13 @@ static FObject Primitives[] =
BytevectorCopyModifyPrimitive,
BytevectorAppendPrimitive,
Utf8ToStringPrimitive,
- StringToUtf8Primitive
+ StringToUtf8Primitive,
+ MakeBytestringPrimitive,
+ BytevectorToHexStringPrimitive,
+ HexStringToBytevectorPrimitive,
+ BytevectorToBase64Primitive,
+ Base64ToBytevectorPrimitive,
+ BytestringToListPrimitive
};
void SetupVectors()
diff --git a/test/srfi.scm b/test/srfi.scm
index 4930e3c..93c5497 100644
--- a/test/srfi.scm
+++ b/test/srfi.scm
@@ -3857,3 +3857,364 @@
(test-end))))
|#
+
+;;
+;; ---- SRFI 207: String-notated bytevectors ----
+;;
+
+(import (srfi 207))
+
+(check-equal #u8(65 66 67 68) #u8"ABCD")
+(check-equal #u8(#x9 #x41 #x72 #x74 #x68 #x75 #x72 #xa) #u8"\tArthur\n")
+(check-equal #u8(#x41 #x42 #x3d #x1f #x0d #x43 #x44) #u8"AB\x3d;\x1f;\x0d;CD")
+
+(define (check-read-bytestring s)
+ (bytestring-error?
+ (guard (o (else o))
+ (let ((port (open-input-string s)))
+ (read port)))))
+
+(check-equal #t (check-read-bytestring "#u8\"\\x100;\""))
+(check-equal #t (check-read-bytestring "#u8\"\\x0ABC\""))
+(check-equal #t (check-read-bytestring "#u8\"\\xE000;\""))
+(check-equal #t (check-read-bytestring "#u8\"\\x80;\""))
+
+(check-equal #u8(108 111 114 101 109) (bytestring "lo" #\r #x65 #u8(#x6d)))
+(check-equal #t (equal? (bytestring) (bytevector)))
+
+(check-equal #u8(0 97 98 99 127 0 255 0 127) (make-bytestring '("\x0;abc\x7f;" 0 255 #\x0 #\x7F)))
+
+(define (check-make-bytestring . args)
+ (bytestring-error? (guard (o (else o)) (make-bytestring args))))
+
+(check-equal #t (check-make-bytestring "abc" #x100 "efg"))
+(check-equal #t (check-make-bytestring "abc\x3BB;efg"))
+(check-equal #t (check-make-bytestring "abc" -1 "efg"))
+(check-equal #t (check-make-bytestring "abc" 256 "efg"))
+(check-equal #t (check-make-bytestring "abc" #\x80 "efg"))
+(check-equal #f (check-make-bytestring "abc" #\x10 "efg"))
+
+(check-equal #t (check-make-bytestring "\x3BB;"))
+(check-equal #t (check-make-bytestring #x100))
+
+(check-equal #u8(0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16)
+ (make-bytestring '(#u8(0 1 2 3) #\x4 5 6 7 "\x8;\x9;\xa;" #u8(11 12) 13 14 #\xF #\x10)))
+
+(check-equal #u8(113 114 115 116 117 118 119 120 121 122 123 124 125 126 127)
+ (bytestring #u8(113 114 115 116) 117 #\x76 #\x77 "xyz{|}~" 127))
+
+(check-equal #u8"lorem"
+ (let ((bv (make-bytevector 5)))
+ (make-bytestring! bv 0 '(#x6c #x6f #x72 #x65 #x6d))
+ bv))
+
+(check-equal #u8" lorem "
+ (let ((bv (make-bytevector 9 #x20)))
+ (make-bytestring! bv 2 '("lo" #\r #x65 #u8(#x6d)))
+ bv))
+
+(check-equal #u8" scheme "
+ (let ((bv (make-bytevector 10 #x20)))
+ (make-bytestring! bv 2 '(#\s #\c "he" #u8(#x6d #x65)))
+ bv))
+
+(check-equal #u8(#x46 #x6f #x72 #x64) #u8"Ford")
+(check-equal "466f7264" (bytevector->hex-string #u8"Ford"))
+(check-equal #u8"Zaphod" (hex-string->bytevector "5a6170686f64"))
+
+(check-equal "6c6f72656d" (bytevector->hex-string #u8"lorem"))
+(check-equal #u8"lorem" (hex-string->bytevector "6c6f72656d"))
+(check-equal #t
+ (bytestring-error? (guard (o (else o)) (hex-string->bytevector "c6f72656d"))))
+(check-equal #t
+ (bytestring-error? (guard (o (else o)) (hex-string->bytevector "6czf72656d"))))
+
+(define homer
+ (bytestring "The Man, O Muse, informe, who many a way / \
+ Wound in his wisedome to his wished stay;"))
+
+(define homer64
+ "VGhlIE1hbiwgTyBNdXNlLCBpbmZvcm1lLCB3aG8gbWFueSBhIHdheSAvIFdvd\
+ W5kIGluIGhpcyB3aXNlZG9tZSB0byBoaXMgd2lzaGVkIHN0YXk7")
+
+(define homer64-w
+ "VGhlIE1hb iwgTyBNdXNlL CBpbmZvcm1lL\nCB3aG8gbWF\tueSBhIH\rdheSAvIFdvd\
+ W5kIGluI GhpcyB 3aXNlZ\t\t\nG9tZSB0b yBoaXMgd\t2lzaGVkIHN0YXk7")
+
+(check-equal #t
+ (equal? (hex-string->bytevector (bytevector->hex-string homer)) homer))
+(check-equal #u8() (hex-string->bytevector (bytevector->hex-string #u8())))
+
+(check-equal "bG9yZW0=" (bytevector->base64 (bytestring "lorem")))
+(check-equal "/+//" (bytevector->base64 #u8(#xff #xef #xff)))
+(check-equal "@*@@" (bytevector->base64 #u8(#xff #xef #xff) "*@"))
+(check-equal #t (equal? (bytevector->base64 homer) homer64))
+(check-equal "AQ==" (bytevector->base64 #u8(1)))
+(check-equal "" (bytevector->base64 #u8()))
+(check-equal #u8"lorem" (base64->bytevector "bG9yZW0="))
+(check-equal #u8(#xff #xef #xff) (base64->bytevector "/+//"))
+(check-equal #u8(#xff #xef #xff) (base64->bytevector "@*@@" "*@"))
+(check-equal #t (equal? (base64->bytevector homer64) homer))
+(check-equal #t (equal? (base64->bytevector homer64-w) homer))
+(check-equal #u8(1) (base64->bytevector "AQ=="))
+(check-equal #u8() (base64->bytevector ""))
+;(check-equal #u8() (base64->bytevector "\n\n\n==\t\r\n"))
+
+(check-equal "AQIDBAUG" (bytevector->base64 #u8(1 2 3 4 5 6)))
+(check-equal "QXJ0aHVyIERlbnQ=" (bytevector->base64 #u8"Arthur Dent"))
+(check-equal #u8(#xfb #xff #xfe) (base64->bytevector "+/ /+"))
+
+(check-equal #t
+ (bytestring-error? (guard (o (else o)) (base64->bytevector "bG9@frob"))))
+
+(check-equal "" (bytevector->base64 #u8""))
+(check-equal "Zg==" (bytevector->base64 #u8"f"))
+(check-equal "Zm8=" (bytevector->base64 #u8"fo"))
+(check-equal "Zm9v" (bytevector->base64 #u8"foo"))
+(check-equal "Zm9vYg==" (bytevector->base64 #u8"foob"))
+(check-equal "Zm9vYmE=" (bytevector->base64 #u8"fooba"))
+(check-equal "Zm9vYmFy" (bytevector->base64 #u8"foobar"))
+
+(check-equal #u8"" (base64->bytevector ""))
+(check-equal #u8"f" (base64->bytevector "Zg = ="))
+(check-equal #u8"fo" (base64->bytevector "Zm 8="))
+(check-equal #u8"foo" (base64->bytevector " Zm9v"))
+(check-equal #u8"foob" (base64->bytevector "Zm9vYg =="))
+(check-equal #u8"fooba" (base64->bytevector "Zm9vYmE= "))
+(check-equal #u8"foobar" (base64->bytevector "Zm 9vYmFy "))
+
+(check-equal () (bytestring->list #u8()))
+(check-equal (#\F #\R 0 #\B) (bytestring->list (bytestring 70 82 0 66)))
+(check-equal (7 9 9 10 200) (bytestring->list (bytestring "\a\t\t\n" 200)))
+(check-equal #u8"lorem" (make-bytestring (bytestring->list #u8"lorem")))
+(check-equal #u8"rem" (make-bytestring (bytestring->list #u8"lorem" 2)))
+(check-equal #u8"or" (make-bytestring (bytestring->list #u8"lorem" 1 3)))
+
+(define (generator->list gen)
+ (define (generate gen lst)
+ (let ((val (gen)))
+ (if (eof-object? val)
+ (reverse lst)
+ (generate gen (cons val lst)))))
+ (generate gen '()))
+(check-equal (#x6c #x6f #x72 #x65 #x6d)
+ (let ((g (make-bytestring-generator "lo" #\r #x65 #u8(#x6d))))
+ (generator->list g)))
+(check-equal #t
+ (bytestring-error? (guard (o (else o)) (make-bytestring-generator "abc\x3BB;efg" #\m #\u))))
+(check-equal #t
+ (bytestring-error? (guard (o (else o)) (make-bytestring-generator 89 90 300))))
+#|
+(define test-bstring (bytestring "lorem"))
+
+
+(define (check-selection)
+ (print-header "Running selection tests...")
+
+ (check (bytestring-pad #u8"lorem" (bytevector-length #u8"lorem") #x7a)
+ => #u8"lorem")
+ (check (utf8->string (bytestring-pad #u8"lorem" 8 #x7a))
+ => "zzzlorem")
+ (check (equal? (bytestring-pad #u8"lorem" 8 #\z)
+ (bytestring-pad #u8"lorem" 8 (char->integer #\z)))
+ => #t)
+ (check (bytestring-pad-right #u8"lorem"
+ (bytevector-length #u8"lorem")
+ #x7a)
+ => #u8"lorem")
+ (check (utf8->string (bytestring-pad-right #u8"lorem" 8 #x7a))
+ => "loremzzz")
+ (check (equal? (bytestring-pad-right #u8"lorem" 8 #\z)
+ (bytestring-pad-right #u8"lorem" 8 (char->integer #\z)))
+ => #t)
+
+ (check (bytestring-trim #u8"lorem" always) => #u8())
+ (check (bytestring-trim #u8"lorem" never) => #u8"lorem")
+ (check (bytestring-trim #u8"lorem" (lambda (u8) (< u8 #x70)))
+ => #u8(#x72 #x65 #x6d))
+ (check (bytestring-trim-right #u8"lorem" always) => #u8())
+ (check (bytestring-trim-right #u8"lorem" never) => #u8"lorem")
+ (check (bytestring-trim-right #u8"lorem" (lambda (u8) (< u8 #x70)))
+ => #u8(#x6c #x6f #x72))
+ (check (bytestring-trim-both #u8"lorem" always) => #u8())
+ (check (bytestring-trim-both #u8"lorem" never) => #u8"lorem")
+ (check (bytestring-trim-both #u8"lorem" (lambda (u8) (< u8 #x70)))
+ => #u8(#x72)))
+
+(define (check-replacement)
+ (print-header "Running bytestring-replace tests...")
+
+ (check (bytestring-replace #u8"lorem" (bytestring "mists") 1 5 1 5)
+ => (bytestring "lists"))
+ (check (bytestring-replace #u8"lorem" (bytestring "faded") 2 5 1 5)
+ => (bytestring "loaded"))
+ (check (bytestring-replace (make-bytevector 5)
+ #u8"lorem"
+ 0
+ (bytevector-length #u8"lorem"))
+ => #u8"lorem")
+
+ (let ((bv1 (bytestring "food")) (bv2 (bytestring "od fo")))
+ (check (bytestring-replace bv1 bv2 2 2 0 5) => (bytestring "food food")))
+ (let ((bv1 (bytestring "food food")))
+ (check (bytestring-replace bv1 (bytevector) 2 7 0 0)
+ => (bytestring "food")))
+)
+
+(define (check-comparison)
+ (define short-bstring (bytestring "lore"))
+ (define long-bstring (bytestring "lorem "))
+ (define mixed-case-bstring (bytestring "loreM"))
+ (print-header "Runnng comparison tests...")
+
+ (check (bytestring<? #u8"lorem" #u8"lorem") => #f)
+ (check (bytestring<? short-bstring #u8"lorem") => #t)
+ (check (bytestring<? mixed-case-bstring #u8"lorem") => #t)
+ (check (bytestring>? #u8"lorem" #u8"lorem") => #f)
+ (check (bytestring>? #u8"lorem" short-bstring) => #t)
+ (check (bytestring>? #u8"lorem" mixed-case-bstring) => #t)
+ (check (bytestring<=? #u8"lorem" #u8"lorem") => #t)
+ (check (bytestring<=? short-bstring #u8"lorem") => #t)
+ (check (bytestring<=? mixed-case-bstring #u8"lorem") => #t)
+ (check (bytestring<=? #u8"lorem" mixed-case-bstring) => #f)
+ (check (bytestring<=? long-bstring #u8"lorem") => #f)
+ (check (bytestring>=? #u8"lorem" #u8"lorem") => #t)
+ (check (bytestring>=? #u8"lorem" short-bstring) => #t)
+ (check (bytestring>=? #u8"lorem" mixed-case-bstring) => #t)
+ (check (bytestring>=? mixed-case-bstring #u8"lorem") => #f)
+ (check (bytestring>=? short-bstring #u8"lorem") => #f)
+)
+
+(define (check-searching)
+ (define (eq-r? b) (= b #x72))
+ (define (lt-r? b) (< b #x72))
+ (print-header "Running search tests...")
+
+ (check (bytestring-index #u8"lorem" always) => 0)
+ (check (bytestring-index #u8"lorem" never) => #f)
+ (check (bytestring-index #u8"lorem" always 3) => 3)
+ (check (bytestring-index #u8"lorem" eq-r?) => 2)
+
+ (check (bytestring-index-right #u8"lorem" always) => 4)
+ (check (bytestring-index-right #u8"lorem" never) => #f)
+ (check (bytestring-index-right #u8"lorem" always 3) => 4)
+ (check (bytestring-index-right #u8"lorem" eq-r?) => 2)
+
+ (check (values~>list (bytestring-span #u8"lorem" always))
+ => (list #u8"lorem" (bytevector)))
+ (check (values~>list (bytestring-span #u8"lorem" never))
+ => (list (bytevector) #u8"lorem"))
+ (check (values~>list (bytestring-span #u8"lorem" lt-r?))
+ => (list (bytestring "lo") (bytestring "rem")))
+
+ (check (values~>list (bytestring-break #u8"lorem" always))
+ => (list (bytevector) #u8"lorem"))
+ (check (values~>list (bytestring-break #u8"lorem" never))
+ => (list #u8"lorem" (bytevector)))
+ (check (values~>list (bytestring-break #u8"lorem" eq-r?))
+ => (list (bytestring "lo") (bytestring "rem"))))
+
+(define (check-join-and-split)
+ (define test-segments '(#u8(1) #u8(2) #u8(3)))
+ (print-header "Running joining and splitting tests...")
+
+ (check (bytestring-join test-segments #u8(0)) => #u8(1 0 2 0 3))
+ (check (bytestring-join test-segments #u8(0) 'prefix) => #u8(0 1 0 2 0 3))
+ (check (bytestring-join test-segments #u8(0) 'suffix) => #u8(1 0 2 0 3 0))
+ (check (bytestring-join '() #u8(0)) => #u8())
+ (check (bytestring-join test-segments #\space) => #u8(1 32 2 32 3))
+ (check (bytestring-join test-segments 0) => #u8(1 0 2 0 3))
+ (check (bytestring-join test-segments "AB")
+ => #u8(1 65 66 2 65 66 3))
+ (check (bytestring-join test-segments #u8(7 8)) => #u8(1 7 8 2 7 8 3))
+ (check (catch-bytestring-error
+ (bytestring-join test-segments 300)) => 'bytestring-error)
+ (check (catch-bytestring-error
+ (bytestring-join test-segments "λ")) => 'bytestring-error)
+ (check (catch-bytestring-error
+ (bytestring-join '() #u8(0) 'strict-infix)) => 'bytestring-error)
+ (check (catch-bytestring-error
+ (bytestring-join '() #u8(0) 'foofix)) => 'bytestring-error)
+
+ (check (bytestring-split #u8(1 0 2 0 3) 0 'infix) => test-segments)
+ (check (bytestring-split #u8(0 1 0 2 0 3) 0 'prefix) => test-segments)
+ (check (bytestring-split #u8(1 0 2 0 3 0) 0 'suffix) => test-segments)
+ (check (bytestring-split #u8(0 0) 0) => '(#u8() #u8() #u8()))
+ (check (bytestring-split #u8() 0) => '())
+ (check (catch-bytestring-error
+ (bytestring-split #u8() 0 'foofix)) => 'bytestring-error))
+
+(define (check-io)
+ (print-header "Running I/O tests...")
+
+ (check (%bytestring/IO "lo" #\r #x65 #u8(#x6d)) => #u8"lorem")
+ (check (%bytestring/IO) => #u8())
+ (check (catch-bytestring-error (%bytestring/IO #x100)) => 'bytestring-error)
+ (check (catch-bytestring-error (%bytestring/IO "λ")) => 'bytestring-error)
+
+ ;;; read-textual-bytestring
+
+ (check (parse-SNB/prefix "#u8\"\"") => #u8())
+ (check (parse-SNB/prefix "#u8\"lorem\"") => #u8"lorem")
+ (check (parse-SNB/prefix "#u8\"\\xde;\\xad;\\xf0;\\x0d;\"")
+ => (bytevector #xde #xad #xf0 #x0d))
+ (check (parse-SNB/prefix "#u8\"\\\"\\\\\\a\\b\\t\\n\\r\\\|\"")
+ => (bytestring #\" #\\ #\alarm #\backspace #\tab #\newline #\return #\|))
+ (check (parse-SNB/prefix "#u8\"lor\\\n\te\\ \r\n\tm\"")
+ => #u8"lorem")
+ (check (parse-SNB "\"lorem\"") => #u8"lorem")
+
+ ;; Invalid SNB detection.
+ (check (catch-bytestring-error (parse-SNB/prefix "#u\"lorem\""))
+ => 'bytestring-error)
+ (check (catch-bytestring-error (parse-SNB/prefix "#u8lorem\""))
+ => 'bytestring-error)
+ (check (catch-bytestring-error (parse-SNB/prefix "#u8\"lorem"))
+ => 'bytestring-error)
+ (check (catch-bytestring-error (parse-SNB/prefix "#u8\"lorem"))
+ => 'bytestring-error)
+ (check (catch-bytestring-error (parse-SNB/prefix "#u8\"l\\orem\""))
+ => 'bytestring-error)
+ (check (catch-bytestring-error (parse-SNB/prefix "#u8\"l\\ orem\""))
+ => 'bytestring-error)
+ (check (catch-bytestring-error (parse-SNB/prefix "#u8\"l\\x6frem\""))
+ => 'bytestring-error)
+ (check (catch-bytestring-error (parse-SNB/prefix "#u8\"l\\x6z;rem\""))
+ => 'bytestring-error)
+ (check (catch-bytestring-error (parse-SNB/prefix "#u8\"α equivalence\""))
+ => 'bytestring-error)
+
+ ;;; write-textual-bytestring
+
+ (check (%bytestring->SNB #u8()) => "#u8\"\"")
+ (check (%bytestring->SNB #u8"lorem") => "#u8\"lorem\"")
+ (check (%bytestring->SNB (bytevector #xde #xad #xbe #xef))
+ => "#u8\"\\xde;\\xad;\\xbe;\\xef;\"")
+ (check (%bytestring->SNB
+ (bytestring #\" #\\ #\alarm #\backspace #\tab #\newline #\return #\|))
+ => "#u8\"\\\"\\\\\\a\\b\\t\\n\\r\\\|\"")
+
+ (let ((#u8"lorem"s
+ '(#u8(124 199 173 212 209 232 249 16 198 32 123 111 130 92 64 155)
+ #u8(50 133 193 27 177 105 10 186 61 149 177 105 96 70 223 190)
+ #u8(0 117 226 155 110 0 66 216 27 129 187 81 17 210 71 152)
+ #u8(123 31 159 25 100 135 246 47 249 137 243 241 45 241 240 221)
+ #u8(207 186 70 110 118 231 79 195 153 253 93 101 126 198 70 235)
+ #u8(138 176 92 152 208 107 28 236 198 254 111 37 241 116 191 206)
+ #u8(221 254 214 90 0 155 132 92 157 246 199 224 224 142 91 114)
+ #u8(228 216 233 80 142 15 158 54 5 85 174 101 111 75 126 209)
+ #u8(191 16 83 245 45 98 72 212 148 202 135 19 213 150 141 121)
+ #u8(41 169 182 96 47 184 16 116 196 251 243 93 81 162 175 140)
+ #u8(85 49 218 138 132 11 27 11 182 27 120 71 254 169 132 166)
+ #u8(89 216 175 23 97 10 237 112 208 195 112 80 198 154 241 254)
+ #u8(187 54 6 57 250 137 129 89 188 19 225 217 168 178 174 129)
+ #u8(88 164 89 40 175 194 108 56 12 124 109 96 148 149 119 109)
+ #u8(241 66 32 115 203 71 128 154 240 111 194 137 73 44 146 3)
+ #u8(177 185 177 233 18 14 178 106 110 109 222 147 111 157 216 208))))
+ (check
+ (every (lambda (bvec)
+ (equal? bvec (parse-SNB/prefix (%bytestring->SNB bvec))))
+ #u8"lorem"s)
+ => #t))
+)
+|#
diff --git a/unix/makefile b/unix/makefile
index 7636d7b..3bab9a5 100644
--- a/unix/makefile
+++ b/unix/makefile
@@ -91,10 +91,12 @@ profile:
debug/base.cpp: debug/txt2cpp makefile ../src/base.scm ../src/srfi-106.scm ../src/srfi-60.scm\
../src/srfi-1.scm ../src/srfi-128.scm ../src/srfi-125.scm ../src/srfi-133.scm\
- ../src/srfi-14.scm ../src/srfi-193.scm ../src/srfi-151.scm ../src/srfi-166.scm
+ ../src/srfi-14.scm ../src/srfi-193.scm ../src/srfi-151.scm ../src/srfi-166.scm\
+ ../src/srfi-207.scm
debug/txt2cpp debug/base.cpp ../src/base.scm ../src/srfi-106.scm ../src/srfi-60.scm\
../src/srfi-1.scm ../src/srfi-128.scm ../src/srfi-125.scm ../src/srfi-133.scm\
- ../src/srfi-14.scm ../src/srfi-193.scm ../src/srfi-151.scm ../src/srfi-166.scm
+ ../src/srfi-14.scm ../src/srfi-193.scm ../src/srfi-151.scm ../src/srfi-166.scm\
+ ../src/srfi-207.scm
debug/foment: debug/foment.o debug/gc.o debug/syncthrd.o debug/compile.o debug/io.o\
debug/synrules.o debug/synpass.o debug/midpass.o debug/genpass.o\
diff --git a/windows/makefile b/windows/makefile
index 003d85e..5e3e9a6 100644
--- a/windows/makefile
+++ b/windows/makefile
@@ -65,10 +65,12 @@ release:
debug\base.cpp: debug\txt2cpp.exe makefile ..\src\base.scm ..\src\srfi-106.scm ..\src\srfi-60.scm\
..\src\srfi-1.scm ..\src\srfi-128.scm ..\src\srfi-125.scm ..\src\srfi-133.scm\
- ..\src\srfi-14.scm ..\src\srfi-193.scm ..\src\srfi-151.scm ..\src\srfi-166.scm
+ ..\src\srfi-14.scm ..\src\srfi-193.scm ..\src\srfi-151.scm ..\src\srfi-166.scm\
+ ..\src\srfi-207.scm
debug\txt2cpp debug\base.cpp ..\src\base.scm ..\src\srfi-106.scm ..\src\srfi-60.scm\
..\src\srfi-1.scm ..\src\srfi-128.scm ..\src\srfi-125.scm ..\src\srfi-133.scm\
- ..\src\srfi-14.scm ..\src\srfi-193.scm ..\src\srfi-151.scm ..\src\srfi-166.scm
+ ..\src\srfi-14.scm ..\src\srfi-193.scm ..\src\srfi-151.scm ..\src\srfi-166.scm\
+ ..\src\srfi-207.scm
debug\foment.exe: debug\foment.obj debug\gc.obj debug\syncthrd.obj debug\compile.obj debug\io.obj\
debug\synrules.obj debug\synpass.obj debug\midpass.obj debug\genpass.obj\