on master help sign in
Add Data.Buffer

This supports mutable buffers of Bits8, including reading and writing files. Intended to support manipulating chunks of binary data for serialisation, sending over networks, etc. Only some very basic primitives at the moment.

commit4a10e276b292282d817778e0474bef5378a78b8b authorEdwin Brady <ecb10@st-andrews.ac.uk> dateSat 25 Mar 2017, 19:10 (10 years ago) parentc15b95d
10 files changed+276−3
CHANGELOG.md+5 libs/base/Data/Buffer.idr+119 libs/base/base.ipkg+1−1 rts/Makefile+2−2 rts/idris_buffer.c+86 rts/idris_buffer.h+22 test/TestData.hs+2 test/buffer001/buffer001.idr+28 test/buffer001/expected+7 test/buffer001/run+4
CHANGELOG.md+5
@@ -1,5 +1,10 @@
11# New in 0.99.2
22
3+## Library Updates
4+
5++ Added `Data.Buffer` to `base`. This allows basic manipulation of mutable
6+ buffers of `Bits8`, including reading from and writing to files.
7+
38## Tool Updates
49
510+ Idris now checks the list of packages specified at the command line
libs/base/Data/Buffer.idrnew file+119
@@ -0,0 +1,119 @@
1+module Data.Buffer
2+
3+%include C "idris_buffer.h"
4+
5+||| A buffer is a pointer to a sized, unstructured, mutable chunk of memory
6+export
7+record Buffer where
8+ constructor MkBuffer
9+ ||| Raw bytes, as a pointer to a block of memory
10+ rawdata : ManagedPtr -- let Idris run time manage the memory
11+ ||| Cached size of block
12+ buf_size : Int
13+ ||| Next location to read/write (e.g. when reading from file)
14+ location : Int
15+
16+||| Create a new buffer 'size' bytes long. Returns 'Nothing' if allocation
17+||| fails
18+export
19+newBuffer : (size : Int) -> IO (Maybe Buffer)
20+newBuffer size = do bptr <- foreign FFI_C "idris_newBuffer" (Int -> IO Ptr)
21+ size
22+ bad <- nullPtr bptr
23+ if bad then pure Nothing
24+ else pure (Just (MkBuffer (prim__registerPtr bptr (size + 8)) size 0))
25+
26+||| Reset the 'next location' pointer of the buffer to 0.
27+||| The 'next location' pointer gives the location for the next file read/write
28+||| so resetting this means you can write it again
29+export
30+resetBuffer : Buffer -> Buffer
31+resetBuffer buf = record { location = 0 } buf
32+
33+||| Return the space available in the buffer
34+export
35+rawSize : Buffer -> IO Int
36+rawSize b = foreign FFI_C "idris_getBufferSize" (ManagedPtr -> IO Int) (rawdata b)
37+
38+export
39+size : Buffer -> Int
40+size b = buf_size b
41+
42+||| Set the byte at position 'loc' to 'val'.
43+||| Does nothing if the location is outside the bounds of the buffer
44+export
45+setByte : Buffer -> (loc : Int) -> (val : Bits8) -> IO ()
46+setByte b loc val
47+ = foreign FFI_C "idris_setBufferByte" (ManagedPtr -> Int -> Bits8 -> IO ())
48+ (rawdata b) loc val
49+
50+||| Set the byte at position 'loc' to 'val'.
51+||| Does nothing if the location is out of bounds of the buffer, or the string
52+||| is too long for the location
53+export
54+setString : Buffer -> Int -> String -> IO ()
55+setString b loc val
56+ = foreign FFI_C "idris_setBufferString" (ManagedPtr -> Int -> String -> IO ())
57+ (rawdata b) loc val
58+
59+||| Copy data from 'src' to 'dest'. Reads 'len' bytes starting at position
60+||| 'start' in 'src', and writes them starting at position 'loc' in 'dest'.
61+||| Does nothing if a location is out of bounds, or there is not enough room
62+export
63+copyData : (src : Buffer) -> (start, len : Int) ->
64+ (dest : Buffer) -> (loc : Int) -> IO ()
65+copyData src start len dest loc
66+ = foreign FFI_C "idris_copyBuffer" (ManagedPtr -> Int -> Int -> ManagedPtr -> Int -> IO ())
67+ (rawdata src) start len (rawdata dest) loc
68+
69+||| Return the value at the given location in the buffer
70+export
71+getByte : Buffer -> (loc : Int) -> IO Bits8
72+getByte b loc
73+ = foreign FFI_C "idris_getBufferByte" (ManagedPtr -> Int -> IO Bits8)
74+ (rawdata b) loc
75+
76+||| Read 'maxbytes' into the buffer from a file, returning a new
77+||| buffer with the 'locaton' pointer moved along
78+export
79+readBufferFromFile : File -> Buffer -> (maxbytes : Int) -> IO Buffer
80+readBufferFromFile (FHandle h) buf max
81+ = do numread <- foreign FFI_C "idris_readBuffer" (Ptr -> ManagedPtr -> Int -> Int -> IO Int)
82+ h (rawdata buf) (location buf) max
83+ pure (record { location $= (+numread) } buf)
84+
85+||| Write 'maxbytes' from the buffer from a file, returning a new
86+||| buffer with the 'locaton' pointer moved along
87+export
88+writeBufferToFile : File -> Buffer -> (maxbytes : Int) -> IO Buffer
89+writeBufferToFile (FHandle h) buf max
90+ = do let maxwrite = size buf - location buf
91+ let max' = if maxwrite < max then maxwrite else max
92+ foreign FFI_C "idris_writeBuffer" (Ptr -> ManagedPtr -> Int -> Int -> IO ())
93+ h (rawdata buf) (location buf) max'
94+ pure (record { location $= (+max') } buf)
95+
96+||| Return the contents of the buffer as a list
97+export
98+bufferData : Buffer -> IO (List Bits8)
99+bufferData b = do let len = size b
100+ unpackTo [] len
101+ where unpackTo : List Bits8 -> Int -> IO (List Bits8)
102+ unpackTo acc 0 = pure acc
103+ unpackTo acc loc = do val <- getByte b (loc - 1)
104+ unpackTo (val :: acc)
105+ (assert_smaller loc (loc - 1))
106+
107+||| Create a new buffer, copying the contents of the old buffer to the new.
108+||| Returns 'Nothing' if resizing fails
109+export
110+resizeBuffer : Buffer -> Int -> IO (Maybe Buffer)
111+resizeBuffer old newsize
112+ = do Just buf <- newBuffer newsize
113+ | Nothing => pure Nothing
114+ -- If the new buffer is smaller than the old one, just copy what
115+ -- fits
116+ let oldsize = size old
117+ let len = if newsize < oldsize then newsize else oldsize
118+ copyData old 0 len buf 0
119+ pure (Just buf)
libs/base/base.ipkg+1−1
@@ -21,7 +21,7 @@ modules = System,
2121 Data.Nat.Views,
2222 Data.Primitives.Views,
2323 Data.String.Views,
24− Data.So, Data.String,
24+ Data.So, Data.String, Data.Buffer,
2525
2626 Control.Isomorphism,
2727 Control.Monad.Identity,
rts/Makefile+2−2
@@ -2,10 +2,10 @@ include ../config.mk
22
33OBJS = idris_rts.o idris_heap.o idris_gc.o idris_gmp.o idris_bitstring.o \
44 idris_opts.o idris_stats.o idris_utf8.o idris_stdfgn.o mini-gmp.o \
5− getline.o
5+ idris_buffer.o getline.o
66HDRS = idris_rts.h idris_heap.h idris_gc.h idris_gmp.h idris_bitstring.h \
77 idris_opts.h idris_stats.h mini-gmp.h idris_stdfgn.h idris_net.h \
8− idris_utf8.h getline.h
8+ idris_buffer.h idris_utf8.h getline.h
99CFLAGS := $(CFLAGS)
1010CFLAGS += $(GMP_INCLUDE_DIR) $(GMP) -DIDRIS_TARGET_OS="\"$(OS)\""
1111CFLAGS += -DIDRIS_TARGET_TRIPLE="\"$(MACHINE)\""
rts/idris_buffer.cnew file+86
@@ -0,0 +1,86 @@
1+#include "idris_rts.h"
2+#include "idris_buffer.h"
3+
4+typedef struct {
5+ int size;
6+ uint8_t* data;
7+} Buffer;
8+
9+void* idris_newBuffer(int bytes) {
10+ Buffer* buf = malloc(sizeof(Buffer) + bytes*sizeof(uint8_t));
11+ if (buf == NULL) {
12+ return NULL;
13+ }
14+
15+ buf->size = bytes;
16+ buf->data = (uint8_t*)(buf+1);
17+ memset(buf->data, 0, bytes);
18+
19+ return buf;
20+}
21+
22+void idris_copyBuffer(void* from, int start, int len,
23+ void* to, int loc) {
24+ Buffer* bfrom = from;
25+ Buffer* bto = to;
26+
27+ if (loc >= 0 && loc+len <= bto->size) {
28+ memcpy((bto->data)+loc, (bfrom->data)+start, len);
29+ }
30+}
31+
32+int idris_getBufferSize(void* buffer) {
33+ return ((Buffer*)buffer)->size;
34+}
35+
36+void idris_setBufferByte(void* buffer, int loc, uint8_t byte) {
37+ Buffer* b = buffer;
38+ if (loc >= 0 && loc < b->size) {
39+ b->data[loc] = byte;
40+ }
41+}
42+
43+void idris_setBufferString(void* buffer, int loc, char* str) {
44+ Buffer* b = buffer;
45+ int len = strlen(str);
46+
47+ if (loc >= 0 && loc+len <= b->size) {
48+ memcpy((b->data)+loc, str, len);
49+ }
50+}
51+
52+uint8_t idris_getBufferByte(void* buffer, int loc) {
53+ Buffer* b = buffer;
54+ if (loc >= 0 && loc < b->size) {
55+ return b->data[loc];
56+ } else {
57+ return 0;
58+ }
59+}
60+
61+int idris_readBuffer(FILE* h, void* buffer, int loc, int max) {
62+ Buffer* b = buffer;
63+ size_t len;
64+
65+ if (loc >= 0 && loc < b->size) {
66+ if (loc + max > b->size) {
67+ max = b->size - loc;
68+ }
69+ len = fread((b->data)+loc, sizeof(uint8_t), (size_t)max, h);
70+ return len;
71+ } else {
72+ return 0;
73+ }
74+}
75+
76+void idris_writeBuffer(FILE* h, void* buffer, int loc, int len) {
77+ Buffer* b = buffer;
78+
79+ if (loc >= 0 && loc < b->size) {
80+ if (loc + len > b->size) {
81+ len = b->size - loc;
82+ }
83+ fwrite((b->data)+loc, sizeof(uint8_t), len, h);
84+ }
85+}
86+
rts/idris_buffer.hnew file+22
@@ -0,0 +1,22 @@
1+#ifndef __BUFFER_H
2+#define __BUFFER_H
3+
4+#include <stdlib.h>
5+#include <stdio.h>
6+
7+void* idris_newBuffer(int bytes);
8+
9+int idris_getBufferSize(void* buffer);
10+
11+void idris_setBufferByte(void* buffer, int loc, uint8_t byte);
12+void idris_setBufferString(void* buffer, int loc, char* str);
13+
14+void idris_copyBuffer(void* from, int start, int len,
15+ void* to, int loc);
16+
17+int idris_readBuffer(FILE* h, void* buffer, int loc, int max);
18+void idris_writeBuffer(FILE* h, void* buffer, int loc, int len);
19+
20+uint8_t idris_getBufferByte(void* buffer, int loc);
21+
22+#endif
test/TestData.hs+2
@@ -77,6 +77,8 @@ testFamiliesData = [
7777 ( 2, ANY )]),
7878 ("bounded", "Bounded",
7979 [ ( 1, ANY )]),
80+ ("buffer", "Buffer",
81+ [ ( 1, C_CG )]),
8082 ("corecords", "Corecords",
8183 [ ( 1, ANY ),
8284 ( 2, ANY )]),
test/buffer001/buffer001.idrnew file+28
@@ -0,0 +1,28 @@
1+import Data.Buffer
2+
3+main : IO ()
4+main = do Just buf <- newBuffer 40
5+ printLn (size buf)
6+ setByte buf 5 42
7+ setString buf 20 "Hello world!"
8+ printLn !(bufferData buf)
9+ Just buf2 <- resizeBuffer buf 50
10+ putStrLn "Resized"
11+ printLn !(bufferData buf2)
12+
13+ putStrLn "Writing to file"
14+ Right h <- openFile "test.buf" WriteTruncate
15+ writeBufferToFile h buf (size buf)
16+ closeFile h
17+
18+ putStrLn "Reading from file twice"
19+ Just buf3 <- newBuffer 80
20+
21+ Right h <- openFile "test.buf" Read
22+ buf3 <- readBufferFromFile h buf3 (size buf3)
23+ closeFile h
24+ Right h <- openFile "test.buf" Read
25+ readBufferFromFile h buf3 (size buf3)
26+ closeFile h
27+
28+ printLn !(bufferData buf3)
test/buffer001/expectednew file+7
@@ -0,0 +1,7 @@
1+40
2+[00, 00, 00, 00, 00, 2A, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 48, 65, 6C, 6C, 6F, 20, 77, 6F, 72, 6C, 64, 21, 00, 00, 00, 00, 00, 00, 00, 00]
3+Resized
4+[00, 00, 00, 00, 00, 2A, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 48, 65, 6C, 6C, 6F, 20, 77, 6F, 72, 6C, 64, 21, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00]
5+Writing to file
6+Reading from file twice
7+[00, 00, 00, 00, 00, 2A, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 48, 65, 6C, 6C, 6F, 20, 77, 6F, 72, 6C, 64, 21, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 2A, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 48, 65, 6C, 6C, 6F, 20, 77, 6F, 72, 6C, 64, 21, 00, 00, 00, 00, 00, 00, 00, 00]
test/buffer001/runnew file+4
@@ -0,0 +1,4 @@
1+#!/usr/bin/env bash
2+${IDRIS:-idris} $@ buffer001.idr -o buffer001
3+./buffer001
4+rm -f buffer001 test.buf *.ibc