summaryrefslogtreecommitdiffstats
path: root/chksum.c
diff options
context:
space:
mode:
authorKaz Kylheku <kaz@kylheku.com>2023-07-09 09:00:26 -0700
committerKaz Kylheku <kaz@kylheku.com>2023-07-09 09:00:26 -0700
commitfeb8eefb2fc97e5f8ccc831f28ef10e67717ec22 (patch)
tree4ef3b27f5372d1ce1b0d74f6a1ef48494946628c /chksum.c
parentf48e22cfb4de514a02ef20da8ff863a175c3dc87 (diff)
downloadtxr-feb8eefb2fc97e5f8ccc831f28ef10e67717ec22.tar.gz
txr-feb8eefb2fc97e5f8ccc831f28ef10e67717ec22.tar.bz2
txr-feb8eefb2fc97e5f8ccc831f28ef10e67717ec22.zip
chksum: generate with TXR.
A giant macro that generates numerous functions and variables is hell for anyone who has to debug the code. The right approach is external code generation, where the result is a clean source file that is checked into the repository. * genchksum.txr: New file. * chksum.c: Now generated. (chksum_impl): Macro and its invocations removed. (sha1_stream_impl, sha1_stream, sha1_szmax_upd, sha1_buf, sha1_str, sha1, sha1_ops, sha1_begin, sha1_utf8_byte_callback, sha1_hash, sha1_end): These are directly defined again. (sha256_stream_impl, sha256_stream, sha256_szmax_upd, sha256_buf, sha256_str, sha256, sha256_ops, sha256_begin, sha256_utf8_byte_callback, sha256_hash, sha256_end): Likewise. generated by macro. md5_stream_impl, md5_stream, md5_szmax_upd, md5_buf, md5_str, md5, md5_ops, md5_begin, md5_utf8_byte_callback, md5_hash, md5_end): Likewise.
Diffstat (limited to 'chksum.c')
-rw-r--r--chksum.c765
1 files changed, 552 insertions, 213 deletions
diff --git a/chksum.c b/chksum.c
index 2db37b17..fa7c5691 100644
--- a/chksum.c
+++ b/chksum.c
@@ -1,3 +1,5 @@
+/* This file is generated by genchksum.txr */
+
/* Copyright 2019-2023
* Kaz Kylheku <kaz@kylheku.com>
* Vancouver, Canada
@@ -68,223 +70,560 @@ static val chksum_ensure_buf(val self, val buf_in,
}
}
-#define chksum_impl(nameroot, ctx_type, \
- hash_name, digest_len, chksum_init, \
- chksum_update, chksum_final) \
- \
- static void nameroot ## _stream_impl(val stream, \
- val nbytes, \
- unsigned char *hash, \
- val self) \
- { \
- ctx_type ctx; \
- val buf = iobuf_get(); \
- val bfsz = length_buf(buf); \
- chksum_init(&ctx); \
- \
- if (null_or_missing_p(nbytes)) { \
- for (;;) { \
- val read = fill_buf(buf, zero, stream); \
- cnum rd = c_num(read, self); \
- \
- if (!rd) \
- break; \
- \
- chksum_update(&ctx, buf->b.data, rd); \
- } \
- } else { \
- while (ge(nbytes, bfsz)) { \
- val read = fill_buf(buf, zero, stream); \
- cnum rd = c_num(read, self); \
- \
- if (zerop(read)) \
- break; \
- \
- chksum_update(&ctx, buf->b.data, rd); \
- nbytes = minus(nbytes, read); \
- } \
- \
- buf_set_length(buf, nbytes, nil); \
- \
- { \
- val read = fill_buf(buf, zero, stream); \
- cnum rd = c_num(read, self); \
- if (rd) \
- chksum_update(&ctx, buf->b.data, rd); \
- } \
- } \
- \
- chksum_final(&ctx, hash); \
- iobuf_put(buf); \
- } \
- \
- val nameroot ## _stream(val stream, val nbytes, val buf_in) \
- { \
- val self = lit(#nameroot "-stream"); \
- unsigned char *hash; \
- val buf = chksum_ensure_buf(self, buf_in, \
- num_fast(digest_len), \
- &hash, lit(hash_name)); \
- nameroot ## _stream_impl(stream, nbytes, hash, self); \
- return buf; \
- } \
- \
- static void nameroot ## _szmax_upd(ctx_type *pctx, \
- mem_t *data, ucnum len) \
- { \
- const size_t szmax = convert(size_t, -1) / 4 + 1; \
- while (len >= szmax) { \
- chksum_update(pctx, data, szmax); \
- data += szmax; \
- len -= szmax; \
- } \
- if (len > 0) \
- chksum_update(pctx, data, len); \
- } \
- \
- static void nameroot ## _buf(val buf, unsigned char *hash, \
- val self) \
- { \
- ctx_type ctx; \
- chksum_init(&ctx); \
- nameroot ## _szmax_upd(&ctx, buf->b.data, \
- c_unum(buf->b.len, self)); \
- chksum_final(&ctx, hash); \
- } \
- \
- static void nameroot ## _str(val str, unsigned char *hash, \
- val self) \
- { \
- char *s = utf8_dup_to(c_str(str, self)); \
- ctx_type ctx; \
- chksum_init(&ctx); \
- chksum_update(&ctx, coerce(const unsigned char *, s), \
- strlen(s)); \
- free(s); \
- chksum_final(&ctx, hash); \
- } \
- \
- val nameroot(val obj, val buf_in) \
- { \
- val self = lit(#nameroot); \
- unsigned char *hash; \
- val buf = chksum_ensure_buf(self, buf_in, \
- num_fast(digest_len), \
- &hash, lit(hash_name)); \
- switch (type(obj)) { \
- case STR: \
- case LSTR: \
- case LIT: \
- nameroot ## _str(obj, hash, self); \
- return buf; \
- case BUF: \
- nameroot ## _buf(obj, hash, self); \
- return buf; \
- default: \
- uw_throwf(error_s, \
- lit("~a: cannot hash ~s, " \
- "only buffer and strings"), \
- self, obj, nao); \
- } \
- } \
- \
- static struct cobj_ops nameroot ## _ops = \
- cobj_ops_init(cobj_equal_handle_op, \
- cobj_print_op, \
- cobj_destroy_free_op, \
- cobj_mark_op, \
- cobj_handle_hash_op); \
- \
- val nameroot ## _begin(void) \
- { \
- ctx_type *pctx = coerce(ctx_type *, \
- chk_malloc(sizeof *pctx)); \
- chksum_init(pctx); \
- return cobj(coerce(mem_t *, pctx), \
- nameroot ## _ctx_cls, &nameroot ## _ops); \
- } \
- \
- static int nameroot ## _utf8_byte_callback(int b, \
- mem_t *ctx) \
- { \
- ctx_type *pctx = coerce(ctx_type *, ctx); \
- unsigned char uc = b; \
- chksum_update(pctx, &uc, 1); \
- return 1; \
- } \
- \
- val nameroot ## _hash(val ctx, val obj) \
- { \
- val self = lit(#nameroot "-hash"); \
- ctx_type *pctx = coerce(ctx_type *, \
- cobj_handle(self, ctx, \
- nameroot ## \
- _ctx_cls)); \
- switch (type(obj)) { \
- case STR: \
- case LSTR: \
- case LIT: \
- { \
- char *str = utf8_dup_to(c_str(obj, self)); \
- chksum_update(pctx, \
- coerce(const unsigned char *, str), \
- strlen(str)); \
- free(str); \
- } \
- break; \
- case BUF: \
- nameroot ## _szmax_upd(pctx, obj->b.data, \
- c_unum(obj->b.len, self)); \
- break; \
- case CHR: \
- utf8_encode(c_ch(obj), \
- nameroot ## _utf8_byte_callback, \
- coerce(mem_t *, pctx)); \
- break; \
- case NUM: \
- { \
- cnum n = c_num(obj, self); \
- unsigned char uc = n; \
- if (n < 0 || n > 255) \
- uw_throwf(error_s, \
- lit("~a: byte value ~s out of range"), \
- self, obj, nao); \
- chksum_update(pctx, &uc, 1); \
- } \
- break; \
- default: \
- uw_throwf(error_s, lit("~a: cannot hash ~s, " \
- "only buffer and strings"), \
- self, obj, nao); \
- } \
- \
- return obj; \
- } \
- \
- val nameroot ## _end(val ctx, val buf_in) \
- { \
- val self = lit(#nameroot "-end"); \
- unsigned char *hash; \
- ctx_type *pctx = coerce(ctx_type *, \
- cobj_handle(self, ctx, \
- nameroot ## \
- _ctx_cls)); \
- val buf = chksum_ensure_buf(self, buf_in, \
- num_fast(digest_len), \
- &hash, lit(hash_name)); \
- chksum_final(pctx, hash); \
- chksum_init(pctx); \
- return buf; \
+static void sha1_stream_impl(val stream, val nbytes,
+ unsigned char *hash, val self)
+{
+ SHA1_t ctx;
+ val buf = iobuf_get();
+ val bfsz = length_buf(buf);
+ SHA1_init(&ctx);
+
+ if (null_or_missing_p(nbytes)) {
+ for (;;) {
+ val read = fill_buf(buf, zero, stream);
+ cnum rd = c_num(read, self);
+
+ if (!rd)
+ break;
+
+ SHA1_update(&ctx, buf->b.data, rd);
+ }
+ } else {
+ while (ge(nbytes, bfsz)) {
+ val read = fill_buf(buf, zero, stream);
+ cnum rd = c_num(read, self);
+
+ if (zerop(read))
+ break;
+
+ SHA1_update(&ctx, buf->b.data, rd);
+ nbytes = minus(nbytes, read);
+ }
+
+ buf_set_length(buf, nbytes, nil);
+
+ {
+ val read = fill_buf(buf, zero, stream);
+ cnum rd = c_num(read, self);
+ if (rd)
+ SHA1_update(&ctx, buf->b.data, rd);
+ }
+ }
+
+ SHA1_final(&ctx, hash);
+ iobuf_put(buf);
+}
+
+val sha1_stream(val stream, val nbytes, val buf_in)
+{
+ val self = lit("sha1-stream");
+ unsigned char *hash;
+ val buf = chksum_ensure_buf(self, buf_in,
+ num_fast(SHA1_DIGEST_LENGTH),
+ &hash, lit("SHA-1"));
+ sha1_stream_impl(stream, nbytes, hash, self);
+ return buf;
+}
+
+static void sha1_szmax_upd(SHA1_t *pctx, mem_t *data, ucnum len)
+{
+ const size_t szmax = convert(size_t, -1) / 4 + 1;
+ while (len >= szmax) {
+ SHA1_update(pctx, data, szmax);
+ data += szmax;
+ len -= szmax;
+ }
+ if (len > 0)
+ SHA1_update(pctx, data, len);
+}
+
+static void sha1_buf(val buf, unsigned char *hash, val self)
+{
+ SHA1_t ctx;
+ SHA1_init(&ctx);
+ sha1_szmax_upd(&ctx, buf->b.data, c_unum(buf->b.len, self));
+ SHA1_final(&ctx, hash);
+}
+
+static void sha1_str(val str, unsigned char *hash, val self)
+{
+ char *s = utf8_dup_to(c_str(str, self));
+ SHA1_t ctx;
+ SHA1_init(&ctx);
+ SHA1_update(&ctx, coerce(const unsigned char *, s), strlen(s));
+ free(s);
+ SHA1_final(&ctx, hash);
+}
+
+val sha1(val obj, val buf_in)
+{
+ val self = lit("sha1");
+ unsigned char *hash;
+ val buf = chksum_ensure_buf(self, buf_in,
+ num_fast(SHA1_DIGEST_LENGTH),
+ &hash, lit("SHA-1"));
+ switch (type(obj)) {
+ case STR:
+ case LSTR:
+ case LIT:
+ sha1_str(obj, hash, self);
+ return buf;
+ case BUF:
+ sha1_buf(obj, hash, self);
+ return buf;
+ default:
+ uw_throwf(error_s,
+ lit("~a: cannot hash ~s, "
+ "only buffer and strings"),
+ self, obj, nao);
}
+}
-chksum_impl(sha1, SHA1_t, "SHA-1", SHA1_DIGEST_LENGTH,
- SHA1_init, SHA1_update, SHA1_final);
+static struct cobj_ops sha1_ops = cobj_ops_init(cobj_equal_handle_op,
+ cobj_print_op,
+ cobj_destroy_free_op,
+ cobj_mark_op,
+ cobj_handle_hash_op);
-chksum_impl(sha256, SHA256_t, "SHA-256", SHA256_DIGEST_LENGTH,
- SHA256_init, SHA256_update, SHA256_final);
+val sha1_begin(void)
+{
+ SHA1_t *pctx = coerce(SHA1_t *, chk_malloc(sizeof *pctx));
+ SHA1_init(pctx);
+ return cobj(coerce(mem_t *, pctx), sha1_ctx_cls, &sha1_ops);
+}
-chksum_impl(md5, MD5_t, "MD5", MD5_DIGEST_LENGTH,
- MD5_init, MD5_update, MD5_final);
+static int sha1_utf8_byte_callback(int b, mem_t *ctx)
+{
+ SHA1_t *pctx = coerce(SHA1_t *, ctx);
+ unsigned char uc = b;
+ SHA1_update(pctx, &uc, 1);
+ return 1;
+}
+
+val sha1_hash(val ctx, val obj)
+{
+ val self = lit("sha1-hash");
+ SHA1_t *pctx = coerce(SHA1_t *,
+ cobj_handle(self, ctx, sha1_ctx_cls));
+ switch (type(obj)) {
+ case STR:
+ case LSTR:
+ case LIT:
+ {
+ char *str = utf8_dup_to(c_str(obj, self));
+ SHA1_update(pctx, coerce(const unsigned char *, str), strlen(str));
+ free(str);
+ }
+ break;
+ case BUF:
+ sha1_szmax_upd(pctx, obj->b.data, c_unum(obj->b.len, self));
+ break;
+ case CHR:
+ utf8_encode(c_ch(obj), sha1_utf8_byte_callback,
+ coerce(mem_t *, pctx));
+ break;
+ case NUM:
+ {
+ cnum n = c_num(obj, self);
+ unsigned char uc = n;
+ if (n < 0 || n > 255)
+ uw_throwf(error_s,
+ lit("~a: byte value ~s out of range"),
+ self, obj, nao);
+ SHA1_update(pctx, &uc, 1);
+ }
+ break;
+ default:
+ uw_throwf(error_s, lit("~a: cannot hash ~s, "
+ "only buffer and strings"),
+ self, obj, nao);
+ }
+
+ return obj;
+}
+
+val sha1_end(val ctx, val buf_in)
+{
+ val self = lit("sha1-end");
+ unsigned char *hash;
+ SHA1_t *pctx = coerce(SHA1_t *,
+ cobj_handle(self, ctx, sha1_ctx_cls));
+ val buf = chksum_ensure_buf(self, buf_in, num_fast(SHA1_DIGEST_LENGTH),
+ &hash, lit("SHA-1"));
+ SHA1_final(pctx, hash);
+ SHA1_init(pctx);
+ return buf;
+}
+
+static void sha256_stream_impl(val stream, val nbytes,
+ unsigned char *hash, val self)
+{
+ SHA256_t ctx;
+ val buf = iobuf_get();
+ val bfsz = length_buf(buf);
+ SHA256_init(&ctx);
+
+ if (null_or_missing_p(nbytes)) {
+ for (;;) {
+ val read = fill_buf(buf, zero, stream);
+ cnum rd = c_num(read, self);
+
+ if (!rd)
+ break;
+
+ SHA256_update(&ctx, buf->b.data, rd);
+ }
+ } else {
+ while (ge(nbytes, bfsz)) {
+ val read = fill_buf(buf, zero, stream);
+ cnum rd = c_num(read, self);
+
+ if (zerop(read))
+ break;
+
+ SHA256_update(&ctx, buf->b.data, rd);
+ nbytes = minus(nbytes, read);
+ }
+
+ buf_set_length(buf, nbytes, nil);
+
+ {
+ val read = fill_buf(buf, zero, stream);
+ cnum rd = c_num(read, self);
+ if (rd)
+ SHA256_update(&ctx, buf->b.data, rd);
+ }
+ }
+
+ SHA256_final(&ctx, hash);
+ iobuf_put(buf);
+}
+
+val sha256_stream(val stream, val nbytes, val buf_in)
+{
+ val self = lit("sha256-stream");
+ unsigned char *hash;
+ val buf = chksum_ensure_buf(self, buf_in,
+ num_fast(SHA256_DIGEST_LENGTH),
+ &hash, lit("SHA-256"));
+ sha256_stream_impl(stream, nbytes, hash, self);
+ return buf;
+}
+
+static void sha256_szmax_upd(SHA256_t *pctx, mem_t *data, ucnum len)
+{
+ const size_t szmax = convert(size_t, -1) / 4 + 1;
+ while (len >= szmax) {
+ SHA256_update(pctx, data, szmax);
+ data += szmax;
+ len -= szmax;
+ }
+ if (len > 0)
+ SHA256_update(pctx, data, len);
+}
+
+static void sha256_buf(val buf, unsigned char *hash, val self)
+{
+ SHA256_t ctx;
+ SHA256_init(&ctx);
+ sha256_szmax_upd(&ctx, buf->b.data, c_unum(buf->b.len, self));
+ SHA256_final(&ctx, hash);
+}
+
+static void sha256_str(val str, unsigned char *hash, val self)
+{
+ char *s = utf8_dup_to(c_str(str, self));
+ SHA256_t ctx;
+ SHA256_init(&ctx);
+ SHA256_update(&ctx, coerce(const unsigned char *, s), strlen(s));
+ free(s);
+ SHA256_final(&ctx, hash);
+}
+
+val sha256(val obj, val buf_in)
+{
+ val self = lit("sha256");
+ unsigned char *hash;
+ val buf = chksum_ensure_buf(self, buf_in,
+ num_fast(SHA256_DIGEST_LENGTH),
+ &hash, lit("SHA-256"));
+ switch (type(obj)) {
+ case STR:
+ case LSTR:
+ case LIT:
+ sha256_str(obj, hash, self);
+ return buf;
+ case BUF:
+ sha256_buf(obj, hash, self);
+ return buf;
+ default:
+ uw_throwf(error_s,
+ lit("~a: cannot hash ~s, "
+ "only buffer and strings"),
+ self, obj, nao);
+ }
+}
+
+static struct cobj_ops sha256_ops = cobj_ops_init(cobj_equal_handle_op,
+ cobj_print_op,
+ cobj_destroy_free_op,
+ cobj_mark_op,
+ cobj_handle_hash_op);
+
+val sha256_begin(void)
+{
+ SHA256_t *pctx = coerce(SHA256_t *, chk_malloc(sizeof *pctx));
+ SHA256_init(pctx);
+ return cobj(coerce(mem_t *, pctx), sha256_ctx_cls, &sha256_ops);
+}
+
+static int sha256_utf8_byte_callback(int b, mem_t *ctx)
+{
+ SHA256_t *pctx = coerce(SHA256_t *, ctx);
+ unsigned char uc = b;
+ SHA256_update(pctx, &uc, 1);
+ return 1;
+}
+
+val sha256_hash(val ctx, val obj)
+{
+ val self = lit("sha256-hash");
+ SHA256_t *pctx = coerce(SHA256_t *,
+ cobj_handle(self, ctx, sha256_ctx_cls));
+ switch (type(obj)) {
+ case STR:
+ case LSTR:
+ case LIT:
+ {
+ char *str = utf8_dup_to(c_str(obj, self));
+ SHA256_update(pctx, coerce(const unsigned char *, str), strlen(str));
+ free(str);
+ }
+ break;
+ case BUF:
+ sha256_szmax_upd(pctx, obj->b.data, c_unum(obj->b.len, self));
+ break;
+ case CHR:
+ utf8_encode(c_ch(obj), sha256_utf8_byte_callback,
+ coerce(mem_t *, pctx));
+ break;
+ case NUM:
+ {
+ cnum n = c_num(obj, self);
+ unsigned char uc = n;
+ if (n < 0 || n > 255)
+ uw_throwf(error_s,
+ lit("~a: byte value ~s out of range"),
+ self, obj, nao);
+ SHA256_update(pctx, &uc, 1);
+ }
+ break;
+ default:
+ uw_throwf(error_s, lit("~a: cannot hash ~s, "
+ "only buffer and strings"),
+ self, obj, nao);
+ }
+
+ return obj;
+}
+
+val sha256_end(val ctx, val buf_in)
+{
+ val self = lit("sha256-end");
+ unsigned char *hash;
+ SHA256_t *pctx = coerce(SHA256_t *,
+ cobj_handle(self, ctx, sha256_ctx_cls));
+ val buf = chksum_ensure_buf(self, buf_in, num_fast(SHA256_DIGEST_LENGTH),
+ &hash, lit("SHA-256"));
+ SHA256_final(pctx, hash);
+ SHA256_init(pctx);
+ return buf;
+}
+
+static void md5_stream_impl(val stream, val nbytes,
+ unsigned char *hash, val self)
+{
+ MD5_t ctx;
+ val buf = iobuf_get();
+ val bfsz = length_buf(buf);
+ MD5_init(&ctx);
+
+ if (null_or_missing_p(nbytes)) {
+ for (;;) {
+ val read = fill_buf(buf, zero, stream);
+ cnum rd = c_num(read, self);
+
+ if (!rd)
+ break;
+
+ MD5_update(&ctx, buf->b.data, rd);
+ }
+ } else {
+ while (ge(nbytes, bfsz)) {
+ val read = fill_buf(buf, zero, stream);
+ cnum rd = c_num(read, self);
+
+ if (zerop(read))
+ break;
+
+ MD5_update(&ctx, buf->b.data, rd);
+ nbytes = minus(nbytes, read);
+ }
+
+ buf_set_length(buf, nbytes, nil);
+
+ {
+ val read = fill_buf(buf, zero, stream);
+ cnum rd = c_num(read, self);
+ if (rd)
+ MD5_update(&ctx, buf->b.data, rd);
+ }
+ }
+
+ MD5_final(&ctx, hash);
+ iobuf_put(buf);
+}
+
+val md5_stream(val stream, val nbytes, val buf_in)
+{
+ val self = lit("md5-stream");
+ unsigned char *hash;
+ val buf = chksum_ensure_buf(self, buf_in,
+ num_fast(MD5_DIGEST_LENGTH),
+ &hash, lit("MD5"));
+ md5_stream_impl(stream, nbytes, hash, self);
+ return buf;
+}
+
+static void md5_szmax_upd(MD5_t *pctx, mem_t *data, ucnum len)
+{
+ const size_t szmax = convert(size_t, -1) / 4 + 1;
+ while (len >= szmax) {
+ MD5_update(pctx, data, szmax);
+ data += szmax;
+ len -= szmax;
+ }
+ if (len > 0)
+ MD5_update(pctx, data, len);
+}
+
+static void md5_buf(val buf, unsigned char *hash, val self)
+{
+ MD5_t ctx;
+ MD5_init(&ctx);
+ md5_szmax_upd(&ctx, buf->b.data, c_unum(buf->b.len, self));
+ MD5_final(&ctx, hash);
+}
+
+static void md5_str(val str, unsigned char *hash, val self)
+{
+ char *s = utf8_dup_to(c_str(str, self));
+ MD5_t ctx;
+ MD5_init(&ctx);
+ MD5_update(&ctx, coerce(const unsigned char *, s), strlen(s));
+ free(s);
+ MD5_final(&ctx, hash);
+}
+
+val md5(val obj, val buf_in)
+{
+ val self = lit("md5");
+ unsigned char *hash;
+ val buf = chksum_ensure_buf(self, buf_in,
+ num_fast(MD5_DIGEST_LENGTH),
+ &hash, lit("MD5"));
+ switch (type(obj)) {
+ case STR:
+ case LSTR:
+ case LIT:
+ md5_str(obj, hash, self);
+ return buf;
+ case BUF:
+ md5_buf(obj, hash, self);
+ return buf;
+ default:
+ uw_throwf(error_s,
+ lit("~a: cannot hash ~s, "
+ "only buffer and strings"),
+ self, obj, nao);
+ }
+}
+
+static struct cobj_ops md5_ops = cobj_ops_init(cobj_equal_handle_op,
+ cobj_print_op,
+ cobj_destroy_free_op,
+ cobj_mark_op,
+ cobj_handle_hash_op);
+
+val md5_begin(void)
+{
+ MD5_t *pctx = coerce(MD5_t *, chk_malloc(sizeof *pctx));
+ MD5_init(pctx);
+ return cobj(coerce(mem_t *, pctx), md5_ctx_cls, &md5_ops);
+}
+
+static int md5_utf8_byte_callback(int b, mem_t *ctx)
+{
+ MD5_t *pctx = coerce(MD5_t *, ctx);
+ unsigned char uc = b;
+ MD5_update(pctx, &uc, 1);
+ return 1;
+}
+
+val md5_hash(val ctx, val obj)
+{
+ val self = lit("md5-hash");
+ MD5_t *pctx = coerce(MD5_t *,
+ cobj_handle(self, ctx, md5_ctx_cls));
+ switch (type(obj)) {
+ case STR:
+ case LSTR:
+ case LIT:
+ {
+ char *str = utf8_dup_to(c_str(obj, self));
+ MD5_update(pctx, coerce(const unsigned char *, str), strlen(str));
+ free(str);
+ }
+ break;
+ case BUF:
+ md5_szmax_upd(pctx, obj->b.data, c_unum(obj->b.len, self));
+ break;
+ case CHR:
+ utf8_encode(c_ch(obj), md5_utf8_byte_callback,
+ coerce(mem_t *, pctx));
+ break;
+ case NUM:
+ {
+ cnum n = c_num(obj, self);
+ unsigned char uc = n;
+ if (n < 0 || n > 255)
+ uw_throwf(error_s,
+ lit("~a: byte value ~s out of range"),
+ self, obj, nao);
+ MD5_update(pctx, &uc, 1);
+ }
+ break;
+ default:
+ uw_throwf(error_s, lit("~a: cannot hash ~s, "
+ "only buffer and strings"),
+ self, obj, nao);
+ }
+
+ return obj;
+}
+
+val md5_end(val ctx, val buf_in)
+{
+ val self = lit("md5-end");
+ unsigned char *hash;
+ MD5_t *pctx = coerce(MD5_t *,
+ cobj_handle(self, ctx, md5_ctx_cls));
+ val buf = chksum_ensure_buf(self, buf_in, num_fast(MD5_DIGEST_LENGTH),
+ &hash, lit("MD5"));
+ MD5_final(pctx, hash);
+ MD5_init(pctx);
+ return buf;
+}
val crc32_stream(val stream, val nbytes, val init)
{