on master help sign in
Add Data.Buffer
4a10e27Edwin Brady
CHANGELOG.md+5
@@ -1,5 +1,10 @@
1# New in 0.99.2
1# New in 0.99.2
2
2
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+
3## Tool Updates
8## Tool Updates
4
9
5+ Idris now checks the list of packages specified at the command line
10+ 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,
21 Data.Nat.Views,
21 Data.Nat.Views,
22 Data.Primitives.Views,
22 Data.Primitives.Views,
23 Data.String.Views,
23 Data.String.Views,
24− Data.So, Data.String,
24+ Data.So, Data.String, Data.Buffer,
25
25
26 Control.Isomorphism,
26 Control.Isomorphism,
27 Control.Monad.Identity,
27 Control.Monad.Identity,
rts/Makefile+2−2
@@ -2,10 +2,10 @@ include ../config.mk
2
2
3OBJS = idris_rts.o idris_heap.o idris_gc.o idris_gmp.o idris_bitstring.o \
3OBJS = idris_rts.o idris_heap.o idris_gc.o idris_gmp.o idris_bitstring.o \
4 idris_opts.o idris_stats.o idris_utf8.o idris_stdfgn.o mini-gmp.o \
4 idris_opts.o idris_stats.o idris_utf8.o idris_stdfgn.o mini-gmp.o \
5− getline.o
5+ idris_buffer.o getline.o
6HDRS = idris_rts.h idris_heap.h idris_gc.h idris_gmp.h idris_bitstring.h \
6HDRS = idris_rts.h idris_heap.h idris_gc.h idris_gmp.h idris_bitstring.h \
7 idris_opts.h idris_stats.h mini-gmp.h idris_stdfgn.h idris_net.h \
7 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
9CFLAGS := $(CFLAGS)
9CFLAGS := $(CFLAGS)
10CFLAGS += $(GMP_INCLUDE_DIR) $(GMP) -DIDRIS_TARGET_OS="\"$(OS)\""
10CFLAGS += $(GMP_INCLUDE_DIR) $(GMP) -DIDRIS_TARGET_OS="\"$(OS)\""
11CFLAGS += -DIDRIS_TARGET_TRIPLE="\"$(MACHINE)\""
11CFLAGS += -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 = [
77 ( 2, ANY )]),
77 ( 2, ANY )]),
78 ("bounded", "Bounded",
78 ("bounded", "Bounded",
79 [ ( 1, ANY )]),
79 [ ( 1, ANY )]),
80+ ("buffer", "Buffer",
81+ [ ( 1, C_CG )]),
80 ("corecords", "Corecords",
82 ("corecords", "Corecords",
81 [ ( 1, ANY ),
83 [ ( 1, ANY ),
82 ( 2, ANY )]),
84 ( 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