Skip to content

Instantly share code, notes, and snippets.

@pascaldekloe
Last active July 25, 2020 18:52
Show Gist options
  • Save pascaldekloe/a06e29c8f6b0ee66c55483321ec97a95 to your computer and use it in GitHub Desktop.
Save pascaldekloe/a06e29c8f6b0ee66c55483321ec97a95 to your computer and use it in GitHub Desktop.
Concept: I/O units with Go
package io
// ReadUniter provides an extension pattern for transaction metadata.
type ReadUniter interface {
Reader
ReadUnits() Units // best effort: possibly zero
}
// WriteUniter provides an extension pattern for transaction metadata.
type WriteUniter interface {
Writer
WriteUnits() Units // best effort: possibly zero
}
// Units provides metadata about the nature of I/O transactions.
// Each inidicator is optional, with the zero value for unknown.
type Units struct {
// BlockSize has the fixed resolution [minimum] in bytes.
// Negative values indicate a variable working size.
BlockSize int
/*
crypto: sha1.BlockSize, cipher.StreamReader, cipher.StreamWriter, …
net: ethernet Interface.MTU
disk sectors:
- Linux: /sys/block/sda/queue/hw_sector_size
- OpenBSD: disklabel(5) DIOCRLDINFO ioctl, e.g., https://cvsweb.openbsd.org/src/sbin/fdisk/disk.c?rev=1.56&content-type=text/x-cvsweb-markup
*/
// DelaySize has a byte threshold for transaction optimization.
// Negative values indicate no delay—payload is transmitted on
// available.
DelaySize int
/*
bufio: Reader.Size, Writer.Size
POSIX: sys/stat.h: stat.st_blksize: preferred I/O block size (a.k.a filesystem segment size)
*/
// MaxSize has the byte limit for a single transaction.
MaxSize int
/*
net: Interface.MTU, TCPConn [AddOverhead]
Plan9: iounit(2)
*/
// OverheadSize has the amount of bytes added to each payload.
// Negative values indicate no overhead. See OverheadFlags.
// How about variable overhead? TCPConn for example…?
OverheadSize int
}
// Inherit applies the Units from an underlying medium on best effort basis.
func (u *Units) Inherit(parent Units) {
switch {
case u.BlockSize == 0,
u.BlockSize < 0 && parent.BlockSize > 0,
u.BlockSize > 0 && parent.BlockSize > u.BlockSize && parent.BlockSize%u.BlockSize == 0:
u.BlockSize = parent.BlockSize
}
switch {
case u.DelaySize == 0,
u.DelaySize < 0 && parent.DelaySize > 0,
u.DelaySize > 0 && parent.DelaySize > u.DelaySize:
u.DelaySize = parent.DelaySize
}
if parent.MaxSize > 0 && parent.MaxSize < u.MaxSize {
u.MaxSize = parent.MaxSize
}
}
// InheritReader uses ReadUnits (when available) on Inherit.
func (u *Units) InheritReader(r Reader) {
if ur, ok := r.(ReadUniter); ok {
u.Inherit(ur.ReadUnits())
}
}
// InheritWriter uses WriteUnits (when available) on Inherit.
func (u *Units) InheritWriter(w Writer) {
if uw, ok := w.(WriteUniter); ok {
u.Inherit(uw.WriteUnits())
}
}
@pascaldekloe
Copy link
Author

+++ b/src/crypto/sha1/sha1.go
@@ -13,6 +13,7 @@ import (
        "encoding/binary"
        "errors"
        "hash"
+       "io"
 )
 
 func init() {
@@ -151,6 +152,11 @@ func (d *digest) Write(p []byte) (nn int, err error) {
        return
 }
 
+// WriteUnits implements io.WriteUniter.
+func (d *digest) WriteUnits() io.Units {
+       return io.Units{BlockSize: chunk, DelaySize: -1, MaxSize: -1}
+}
+++ b/src/bufio/bufio.go
@@ -66,6 +66,13 @@ func NewReader(rd io.Reader) *Reader {
 // Size returns the size of the underlying buffer in bytes.
 func (b *Reader) Size() int { return len(b.buf) }
 
+// ReadUnits implements io.ReadUniter.
+func (b *Reader) ReadUnits() io.Units {
+       u := io.Units{BlockSize: -1, DelaySize: b.Size(), MaxSize: -1}
+       u.InheritReader(b.rd)
+       return u
+}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment