doc: `encoding.md5`; added `io.stream_read_until_full`
authorBrendan Hansen <brendan.f.hansen@gmail.com>
Wed, 29 Mar 2023 03:21:53 +0000 (22:21 -0500)
committerBrendan Hansen <brendan.f.hansen@gmail.com>
Wed, 29 Mar 2023 03:21:53 +0000 (22:21 -0500)
core/hash/md5.onyx
core/io/stream.onyx

index 635a57d0160320de6cd06edd929343c4fb16675c..ab4984e3fd069d2949b60ab59750350eafbcc9ff 100644 (file)
@@ -4,30 +4,31 @@ use core {io, memory, conv}
 use core.intrinsics.wasm {rotl_i32}
 
 
-digest :: #match #local -> MD5_Digest {}
+#doc "Produces an MD5 digest of a string or stream."
+digest :: #match #local {}
 
+#doc "Produces an MD5 digest of a string. This is guaranteed to succeed."
 #overload
 digest :: (x: str) -> MD5_Digest {
-    string_reader, string_stream := io.reader_from_string(x);
-    defer cfree(string_stream);
-    defer delete(&string_reader);
-
-    return digest(&string_reader);
+    string_stream := io.buffer_stream_make(x, write_enabled=false);
+    return digest(&string_stream)?;
 }
 
+#doc "Produces an MD5 digest of a stream. This is not guaranteed to succeed, as the stream may fail part way through."
 #overload
-digest :: (r: &io.Reader) -> MD5_Digest {
+digest :: (s: &io.Stream) -> ?MD5_Digest {
     dig := MD5_Digest.make();
 
     remaining_bytes_to_digest := 0;
 
     bytes_to_digest: [64] u8;
-    while !r->is_empty() {
-        byte_count, err := r->read_bytes(bytes_to_digest);
-        
-        // Exit early to handle the tail case.
-        // This is subject to read_pending errors if the reader
-        // does not have enough bytes and returns read_pending.
+    while true {
+        err, byte_count := io.stream_read(s, bytes_to_digest);
+
+        if err != .None && err != .EOF {
+            return .{};
+        }
+
         if byte_count < 64 {
             remaining_bytes_to_digest = byte_count;
             break;
@@ -36,7 +37,7 @@ digest :: (r: &io.Reader) -> MD5_Digest {
         do_cycle(&dig, bytes_to_digest);
     }
 
-    dig->finish(bytes_to_digest[0..remaining_bytes_to_digest]);
+    dig->_finish(bytes_to_digest[0..remaining_bytes_to_digest]);
     return dig;
 }
 
@@ -59,7 +60,7 @@ MD5_Digest :: struct {
         *self = MD5_Digest.make();
     }
 
-    finish :: (self: &#Self, tail: [] u8) {
+    _finish :: (self: &#Self, tail: [] u8) {
         assert(tail.count < 64, "Tail too long");
 
         bytes_to_digest: [64] u8;
@@ -69,11 +70,9 @@ MD5_Digest :: struct {
         if tail.count >= 56 {
             do_cycle(self, bytes_to_digest, accumulate=false);
             memory.set(~~bytes_to_digest, 0, 64);
-
-        } else {
-            self.bytes_digested += ~~tail.count;
         }
 
+        self.bytes_digested += ~~tail.count;
         *cast(&u64, &bytes_to_digest[56]) = self.bytes_digested * 8;
 
         do_cycle(self, bytes_to_digest, accumulate=false);
index a4610191e921aeda66a8bf7a94219b10b5d847b7..f6de5cb5abec17936a1938a7569904065f9e848e 100644 (file)
@@ -75,6 +75,23 @@ stream_read_byte :: (use s: &Stream) -> (Error, u8) {
     return vtable.read_byte(s);
 }
 
+stream_read_until_full :: (use s: &Stream, buffer: [] u8) -> (Error, u32) {
+    if vtable == null do return .NoVtable, 0;
+    if vtable.read == null_proc do return .NotImplemented, 0;
+
+    bytes_read := 0;
+    while bytes_read < buffer.count {
+        err, r := vtable.read(s, buffer[bytes_read .. buffer.length]);
+        bytes_read += r;
+
+        if err != .ReadPending && err != .ReadLater && err != .None {
+            return err, bytes_read;
+        }
+    }
+
+    return .None, bytes_read;
+}
+
 stream_write :: (use s: &Stream, buffer: [] u8) -> (Error, u32) {
     if vtable == null do return .NoVtable, 0;
     if vtable.write == null_proc do return .NotImplemented, 0;