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;
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;
}
*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;
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);
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;