{
: new_writer
: new_writer2
: write_all
: write_byte
: write
: flush
: with_writer
}

Where

Define (with_writer fd size func)
    Let w (new_writer2 fd size)
    In
    Begin {
        (func w)
        (IO.flush w)
    }

Where

Define (new_writer2 fd size)
    Define (write w bytes start size)
        (write w.state bytes start size)
    Define (write_all w bytes)
        (write_all w.state bytes)
    Define (write_byte w byte)
        (write_byte w.state byte)
    Define (flush w)
        (flush w.state)
    In
    {
    : interface {: write : write_all : write_byte : flush}
    : state (new_writer fd size)
    }

Where

Define (write_all w bytes)
    (write w bytes 0 (CHUNK.size bytes))

Where

Define (write_byte w byte)
    Begin {
        When [(fetch_top w) = (size w)] {
            (flush w)
        }
        Begin {
            Let top (fetch_top w)
            (CHUNK.store_byte w [8 + top] byte)
            (store_top w [top + 1])
        }
    }

Define (write w bytes start count)
    Let fd (fetch_fd w)
    Let size (size w)
    In
    Iterate {start count}
        Let top (fetch_top w)
        In
        Let room [size - top]
        In
        Begin Cond {
        | [count > room]
            (Return
                Begin Cond {
                | [top = 0]
                    Let r (OS.write fd bytes start count)
                    When [r <= 0] {
                        (OS.die "Failed to write bytes.")
                    }
                    (Continue [start + r] [count - r])
                | True
                    (CHUNK.store_bytes w [8 + top] bytes start room)
                    (store_top w size)
                    (flush w)
                    (Continue [start + room] [count - room])
                })
        | True
            (CHUNK.store_bytes w [8 + top] bytes start count)
            (store_top w [top + count])
        }

Where

Define (new_writer fd size)
    Begin {
        Let w (alloc size)
        (init w fd)
        (Return w)
    }

Define (flush w)
    Let fd (fetch_fd w)
    Let top (fetch_top w)
    In
    When [top > 0] {
        Let r (OS.write fd w 8 top)
        When [r != top] {
            (OS.die "Failed to flush buffer.")
        }
        (store_top w 0)
    }

Where

Define (alloc size)
    (CHUNK.new [8 + size])

Define (init w fd)
    Begin {
        (store_fd w fd)
    }

Where

Define (store_fd w fd)
    (CHUNK.store_uint32 w 0 fd)

Define (fetch_fd w)
    (CHUNK.fetch_uint32 w 0)

Define (store_top w top)
    (CHUNK.store_uint32 w 4 top)

Define (fetch_top w)
    (CHUNK.fetch_uint32 w 4)

Define (size w)
    [(CHUNK.size w) - 8]

Where

Let CHUNK Package "chunk"
Let IO Package "io"
Let OS Package "os"
Let STDIO Package "stdio"