├── .gitignore ├── COPYING ├── README.md ├── build.sh ├── clean.sh ├── src ├── Makefile ├── fdreadf.c ├── fdwritef.c ├── fiddle.h ├── freadf.c ├── fwritef.c ├── lenf.c ├── memf-internal.h ├── memf.c ├── memf.h ├── mreadf.c ├── mwritef.c └── sizef.c └── test ├── Makefile ├── mem_skel.c ├── parsertest.c └── ptestgen.c /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.a 3 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015, Robert Clausecker 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are 6 | met: 7 | 8 | 1. Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | 11 | 2. Redistributions in binary form must reproduce the above copyright 12 | notice, this list of conditions and the following disclaimer in the 13 | documentation and/or other materials provided with the distribution. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 16 | IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 17 | TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 18 | PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 19 | HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 20 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 21 | TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 22 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 23 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 24 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | The memf Family of Functions 2 | ============================ 3 | 4 | This project contains an experimental set of functions to marshall data 5 | from binary files into in-memory structures in an easy and portable way. 6 | The interface is similar to that of `scanf` and `printf`: Provide a 7 | structure you want to marshall your data into and call a function from 8 | this family with a pointer to such a structure, a data source / drain 9 | and a string describing the fields of the structure and the rest happens 10 | automatically. No long-winded marshalling code is needed. 11 | 12 | Example Usage 13 | ------------- 14 | 15 | In the following example, I want to marshall the content of a FAT12 boot 16 | sector into a useful format. Here's the structure we are going to use. 17 | Notice that it needs to closely mirror the binary representation: 18 | 19 | struct fat_bootsector { 20 | uint8_t jump_instruction[3]; 21 | uint8_t oem name[8]; 22 | uint16_t bytes_per_sector; 23 | uint8_t sectors_per_cluster; 24 | uint16_t reserved_sectors; 25 | uint8_t fat_copies; 26 | uint16_t max_dirs; 27 | uint16_t sector_count; 28 | uint8_t media_descriptor; 29 | uint16_t sectors_per_fat; 30 | uint16_t sectors_per_head; 31 | uint16_t heads; 32 | uint32_t hidden_sectors; 33 | uint32_t sector_count; 34 | } fat_struct; 35 | 36 | Now we can read a binary image of the MBR into this structure: 37 | 38 | mreadf(mbr, "i3c8chchchhchhhdd", &fat_struct); 39 | 40 | It's really that simple. 41 | 42 | Directives 43 | ---------- 44 | 45 | Supported directives: 46 | 47 | regex meaning 48 | (blank) ignored 49 | c read / write uint8_t 50 | h read / write uint16_t 51 | d read / write uint32_t 52 | l read / write uint64_t 53 | i switch to Intel (little endian) byte order 54 | m switch to Motorola (big endian) byte order 55 | [0-9]+ next item repeated n times 56 | ( begin of group 57 | ) end of group 58 | z skip one byte of input / emit \0 59 | 60 | Changes in byte order do not propagate out of the group they are done 61 | in. A group counts as a single item for repetition. 62 | 63 | Future Extensions 64 | ----------------- 65 | 66 | This is work in progress. A lot of functionality I'd like to have is not 67 | implemented yet. Stay tuned for updates. 68 | 69 | regex meaning 70 | a[0-9]+ align to multiple of number 71 | { begin of substructure 72 | } end of substructure 73 | '[^']*' match against string / emit string; escape ' as '' 74 | "[^"]*" ignore strlen bytes / emit string; escape " as "" 75 | s consume next structure item but do not read / write 76 | r rewind structure 77 | 78 | Build 79 | ----- 80 | 81 | To build this project, execute 82 | 83 | sh build.sh 84 | 85 | in the top source directory. This creates a static library `libmemf.a` 86 | in the `src` directory and then runs the test suite on this library. 87 | This process is subject to change. 88 | 89 | Stability 90 | --------- 91 | 92 | The following things are promised to not change in incompatible ways 93 | as soon as the first stable version of this project is released. 94 | 95 | * The API exposed by `memf.h` 96 | * The syntax of valid (i.e. non-errno setting) formatting strings 97 | 98 | Testability 99 | ----------- 100 | 101 | You might have noticed the absence of a length parameter for the buffer 102 | these functions read from. Such a length parameter is not required for 103 | any usage. The number of bytes read from or written to the buffer does 104 | only depend on the content of the structure description string. The 105 | function `mlenf()` allows you to compute at runtime how many bytes an 106 | invocation of one of the other functions would read or write, the 107 | function `msizef()` likewise allows you to compute the size of the 108 | structure marshalled from. If you want to verify that your formatting 109 | string is correct, use something like this: 110 | 111 | #define MY_FORMATTING_STRING "i3c8chchchhchhhdd" 112 | assert(mlenf(MY_FORMATTING_STRING) == 36); 113 | assert(msizef(MY_FORMATTING_STRING) < sizeof fat_struct); 114 | if (mreadf(mbr, MY_FORMATTING_STRING, &fat_struct) != 36) 115 | /* error handling code here */ 116 | 117 | A first set of unit tests for the parser has been written. More tests 118 | are going to follow soon. 119 | 120 | Project Goals 121 | ------------- 122 | 123 | Portability is the primary concern. All functionality must yield 124 | identical results on all platforms this code is running on. Thus, 125 | functionality like providing a flag for native byte order or types of 126 | platform-dependent width will not be supported. The second concern is 127 | scarcity. The project shall not contain useless functionality and 128 | features that take a lot of code to implement for comparably little gain 129 | shall be omitted. 130 | 131 | Portability 132 | ----------- 133 | 134 | These functions are designed to be portable and self-contained. They 135 | should be suitable for a wide range of hosted and embedded platforms. 136 | 137 | This source code makes use of the library functions `memcpy`, `memset`, 138 | `strtoul`, `fread`, and `fwrite`, the latter two are only required if 139 | you plan to use the functions `freadf` and `fwritef`. The code does not 140 | use any C99 language constructs but assumes the presence of a header 141 | `stdint.h` providing the types `uint8_t`, `uint16_t`, `uint32_t`, and 142 | `uint64_t` with the semantics specified by ISO 9899:1999§7.18.1.1. If 143 | your platform does not provide these types, you can try to make them 144 | yourself. That should be possible unless your platform is really weird. 145 | 146 | This source code makes the following assumptions about the platform it's 147 | running on: 148 | 149 | 1. The following macro computes the *structure alignment* of a type `t`. 150 | The meaning of the result of this macro must be for all scalar `t` 151 | dividable by the minimum alignment requirement for `t`. 152 | 153 | #define structalign(t) offsetof (struct {char; t var;}, var) 154 | 155 | 2. A structure member of type `t` is placed on the lowest unused (i.e. 156 | not occupied by a previous structure member) offset in that structure 157 | such that the offset of `t` from the beginning of the structure is 158 | dividable by `structalign(t)`. 159 | 160 | 3. The value of `structalign(t)` where `t` is an array, structure, or 161 | union type is equal to the maximum of the results of `structalign` 162 | applied to each member of `t`. 163 | 164 | 4. For the `freadf` and `fwritef` function only: `uint8_t` is the same 165 | type as `unsigned char`. If this is not the case, these two functions 166 | may not be usable but the rest is not affected. 167 | 168 | While these assumptions operate outside of what is guaranteed by 169 | ISO 9899 and its successors, they generally hold true on real-world 170 | platforms because specifying structure layout to follow these rules is 171 | a very sensible design decision. This is how all ABIs I know for 172 | byte-addressable machines are designed. Please tell me if the code does 173 | not operate correctly on your machine so I can try to see the way in 174 | which my assumptions are wrong. 175 | 176 | Please remember that this code assumes that it is compiled with the same 177 | ABI as the structure it marshalls to / from. If you want to use options 178 | like -fpack-struct or #pragma pack, you can do that but you need to 179 | compile the memf code with the same options as the rest of your code or 180 | it will most likely not work correctly. 181 | 182 | Authorship 183 | ---------- 184 | 185 | Copyright (c) 2015, Robert Clausecker. 186 | 187 | See file COPYING for details. 188 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | 5 | (cd src ; make ; ) 6 | (cd test ; make ; ) 7 | -------------------------------------------------------------------------------- /clean.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | 5 | (cd src ; make clean ; ) 6 | (cd test ; make clean ; ) 7 | -------------------------------------------------------------------------------- /src/Makefile: -------------------------------------------------------------------------------- 1 | LIB=libmemf.a 2 | CFLAGS=-O2 3 | CC=c99 4 | 5 | $(LIB): $(LIB)(memf.o) $(LIB)(freadf.o) $(LIB)(fwritef.o) \ 6 | $(LIB)(mreadf.o) $(LIB)(mwritef.o) $(LIB)(sizef.o) \ 7 | $(LIB)(lenf.o) $(LIB)(fdreadf.o) $(LIB)(fdwritef.o) 8 | 9 | clean: 10 | rm -f *.o *.a 11 | 12 | .PHONY: clean 13 | -------------------------------------------------------------------------------- /src/fdreadf.c: -------------------------------------------------------------------------------- 1 | #define _POSIX_C_SOURCE 200809L 2 | #include 3 | #include 4 | 5 | #include "memf.h" 6 | #include "memf-internal.h" 7 | 8 | static rwfunc rwfdread; 9 | 10 | /* 11 | * Read count bytes from file to buf. 12 | */ 13 | static size_t 14 | rwfdread(void *file, uint8_t *buf, size_t count) 15 | { 16 | size_t total = 0; 17 | ssize_t n; 18 | 19 | while (total < count) { 20 | total += n = read(*(int*)file, buf + total, count - total); 21 | if (n <= 0) 22 | return (total - n); 23 | } 24 | 25 | return total; 26 | } 27 | 28 | /* 29 | * Read binary data from fd and unmarshall it into repr as described by fstr. 30 | */ 31 | extern size_t 32 | fdreadf(int fd, const char *fstr, void *restrict repr) 33 | { 34 | struct rwfile rwf = { &rwfdread, &fd, READ }; 35 | 36 | return memf(&rwf, fstr, repr); 37 | } 38 | -------------------------------------------------------------------------------- /src/fdwritef.c: -------------------------------------------------------------------------------- 1 | #define _POSIX_C_SOURCE 200809L 2 | #include 3 | #include 4 | 5 | #include "memf.h" 6 | #include "memf-internal.h" 7 | 8 | static rwfunc rwfdwrite; 9 | 10 | /* 11 | * Write count bytes from buf to file. 12 | */ 13 | static size_t 14 | rwfdwrite(void *file, uint8_t *buf, size_t count) 15 | { 16 | size_t total = 0; 17 | ssize_t n; 18 | 19 | while (total < count) { 20 | total += n = write(*(int*)file, buf + total, count - total); 21 | if (n <= 0) 22 | return (total - n); 23 | } 24 | 25 | return total; 26 | } 27 | 28 | /* 29 | * Marshall data in repr into a portable representation and write int fd 30 | */ 31 | extern size_t 32 | fdwritef(int fd, const char *fstr, const void *repr) 33 | { 34 | struct rwfile rwf = { &rwfdwrite, &fd, READ }; 35 | 36 | return memf(&rwf, fstr, (void*)repr); 37 | } 38 | -------------------------------------------------------------------------------- /src/fiddle.h: -------------------------------------------------------------------------------- 1 | #ifndef FIDDLE_H 2 | #define FIDDLE_H 3 | 4 | #include 5 | #include 6 | 7 | /* byte-fiddling functions */ 8 | 9 | /* read/write intel/motorola 16/32/64 */ 10 | static inline uint8_t rm8 (const uint8_t[1]); 11 | static inline uint16_t rm16(const uint8_t[2]); 12 | static inline uint32_t rm32(const uint8_t[4]); 13 | static inline uint64_t rm64(const uint8_t[8]); 14 | static inline uint8_t ri8 (const uint8_t[1]); 15 | static inline uint16_t ri16(const uint8_t[2]); 16 | static inline uint32_t ri32(const uint8_t[4]); 17 | static inline uint64_t ri64(const uint8_t[8]); 18 | 19 | static inline void wm8 (uint8_t[1], uint8_t ); 20 | static inline void wm16(uint8_t[2], uint16_t); 21 | static inline void wm32(uint8_t[4], uint32_t); 22 | static inline void wm64(uint8_t[8], uint64_t); 23 | static inline void wi8 (uint8_t[1], uint8_t ); 24 | static inline void wi16(uint8_t[2], uint16_t); 25 | static inline void wi32(uint8_t[4], uint32_t); 26 | static inline void wi64(uint8_t[8], uint64_t); 27 | 28 | static inline uint64_t 29 | ri64(const uint8_t a[8]) 30 | { 31 | uint64_t x; 32 | 33 | x = (uint64_t)a[0] << 0; 34 | x |= (uint64_t)a[1] << 8; 35 | x |= (uint64_t)a[2] << 16; 36 | x |= (uint64_t)a[3] << 24; 37 | x |= (uint64_t)a[4] << 32; 38 | x |= (uint64_t)a[5] << 40; 39 | x |= (uint64_t)a[6] << 48; 40 | x |= (uint64_t)a[7] << 56; 41 | 42 | return x; 43 | } 44 | 45 | static inline uint64_t 46 | rm64(const uint8_t a[8]) 47 | { 48 | uint64_t x; 49 | 50 | x = (uint64_t)a[0] << 56; 51 | x |= (uint64_t)a[1] << 48; 52 | x |= (uint64_t)a[2] << 40; 53 | x |= (uint64_t)a[3] << 32; 54 | x |= (uint64_t)a[4] << 24; 55 | x |= (uint64_t)a[5] << 16; 56 | x |= (uint64_t)a[6] << 8; 57 | x |= (uint64_t)a[7] << 0; 58 | 59 | return x; 60 | } 61 | 62 | static inline void 63 | wi64(uint8_t a[8], uint64_t x) 64 | { 65 | a[0] = x >> 0; 66 | a[1] = x >> 8; 67 | a[2] = x >> 16; 68 | a[3] = x >> 24; 69 | a[4] = x >> 32; 70 | a[5] = x >> 40; 71 | a[6] = x >> 48; 72 | a[7] = x >> 56; 73 | } 74 | 75 | static inline void 76 | wm64(uint8_t a[8], uint64_t x) 77 | { 78 | a[0] = x >> 56; 79 | a[1] = x >> 48; 80 | a[2] = x >> 40; 81 | a[3] = x >> 32; 82 | a[4] = x >> 24; 83 | a[5] = x >> 16; 84 | a[6] = x >> 8; 85 | a[7] = x >> 0; 86 | } 87 | 88 | static inline uint32_t 89 | ri32(const uint8_t a[4]) 90 | { 91 | uint32_t x; 92 | 93 | x = (uint32_t)a[0] << 0; 94 | x |= (uint32_t)a[1] << 8; 95 | x |= (uint32_t)a[2] << 16; 96 | x |= (uint32_t)a[3] << 24; 97 | 98 | return x; 99 | } 100 | 101 | static inline uint32_t 102 | rm32(const uint8_t a[4]) 103 | { 104 | uint32_t x; 105 | 106 | x = (uint32_t)a[0] << 24; 107 | x |= (uint32_t)a[1] << 16; 108 | x |= (uint32_t)a[2] << 8; 109 | x |= (uint32_t)a[3] << 0; 110 | 111 | return x; 112 | } 113 | 114 | static inline void 115 | wi32(uint8_t a[4], uint32_t x) 116 | { 117 | a[0] = x >> 0; 118 | a[1] = x >> 8; 119 | a[2] = x >> 16; 120 | a[3] = x >> 24; 121 | } 122 | 123 | static inline void 124 | wm32(uint8_t a[4], uint32_t x) 125 | { 126 | a[0] = x >> 24; 127 | a[1] = x >> 16; 128 | a[2] = x >> 8; 129 | a[3] = x >> 0; 130 | } 131 | 132 | static inline uint16_t 133 | ri16(const uint8_t a[2]) 134 | { 135 | uint16_t x; 136 | 137 | x = (uint16_t)a[0] << 0; 138 | x |= (uint16_t)a[1] << 8; 139 | 140 | return x; 141 | } 142 | 143 | static inline uint16_t 144 | rm16(const uint8_t a[2]) 145 | { 146 | uint16_t x; 147 | 148 | x = (uint16_t)a[0] << 8; 149 | x |= (uint16_t)a[1] << 0; 150 | 151 | return x; 152 | } 153 | 154 | static inline void 155 | wi16(uint8_t a[2], uint16_t x) 156 | { 157 | a[0] = x >> 0; 158 | a[1] = x >> 8; 159 | } 160 | 161 | static inline void 162 | wm16(uint8_t a[2], uint16_t x) 163 | { 164 | a[0] = x >> 8; 165 | a[1] = x >> 0; 166 | } 167 | 168 | static inline uint8_t 169 | ri8(const uint8_t a[1]) 170 | { 171 | uint8_t x; 172 | 173 | x = (uint8_t)a[0] << 0; 174 | 175 | return x; 176 | } 177 | 178 | static inline uint8_t 179 | rm8(const uint8_t a[1]) 180 | { 181 | uint8_t x; 182 | 183 | x = (uint8_t)a[0] << 0; 184 | 185 | return x; 186 | } 187 | 188 | static inline void 189 | wi8(uint8_t a[1], uint8_t x) 190 | { 191 | a[0] = x >> 0; 192 | } 193 | 194 | static inline void 195 | wm8(uint8_t a[1], uint8_t x) 196 | { 197 | a[0] = x >> 0; 198 | } 199 | 200 | 201 | 202 | /* hopefully portably alignof macro */ 203 | #define structalign(t) offsetof (struct {char pad; t var;}, var) 204 | /* lowest multiple of a not lower than than idx */ 205 | #define alignto(a, idx) (((idx) + (a) - 1) / (a) * (a)) 206 | 207 | #endif /* FIDDLE_H */ 208 | -------------------------------------------------------------------------------- /src/freadf.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "memf.h" 5 | #include "memf-internal.h" 6 | 7 | static rwfunc rwfread; 8 | 9 | /* 10 | * Write count bytes from buf to file. 11 | */ 12 | static size_t 13 | rwfread(void *file, uint8_t *buf, size_t count) 14 | { 15 | 16 | return fread(buf, 1, count, (FILE*)file); 17 | } 18 | 19 | /* 20 | * Read binary data from a FILE* and marshall it into repr as described by fstr. 21 | */ 22 | extern size_t 23 | freadf(FILE *file, const char *fstr, void *restrict repr) 24 | { 25 | struct rwfile rwf = { &rwfread, file, READ }; 26 | 27 | return memf(&rwf, fstr, repr); 28 | } 29 | -------------------------------------------------------------------------------- /src/fwritef.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "memf.h" 5 | #include "memf-internal.h" 6 | 7 | static rwfunc rwfwrite; 8 | 9 | /* 10 | * write count bytes from buf to file. 11 | */ 12 | static size_t 13 | rwfwrite(void *file, uint8_t *buf, size_t count) 14 | { 15 | 16 | return fwrite(buf, 1, count, (FILE*)file); 17 | } 18 | 19 | /* 20 | * Write binary data to a FILE* and marshall it from repr as described by fstr. 21 | */ 22 | extern size_t 23 | fwritef(FILE *file, const char *fstr, const void *repr) 24 | { 25 | struct rwfile rwf = { &rwfwrite, file, WRITE }; 26 | 27 | return memf(&rwf, fstr, (void*)repr); 28 | } 29 | -------------------------------------------------------------------------------- /src/lenf.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "memf.h" 4 | #include "memf-internal.h" 5 | 6 | /* 7 | * Compute how long the marshalled representation as described by fstr 8 | * is. If the formatting string is invalid, errno will be set to a 9 | * nonzero value and the value returned is invalid. 10 | */ 11 | extern size_t 12 | mlenf(const char *fstr) 13 | { 14 | struct pstate pst; 15 | size_t len = 0; 16 | long rep; 17 | int size; 18 | 19 | memset(&pst, 0, sizeof pst); 20 | 21 | while (parsefstr(fstr, &pst, &size, &rep)) 22 | len += rep << (size < 0 ? 0 : size); 23 | 24 | return len; 25 | } 26 | -------------------------------------------------------------------------------- /src/memf-internal.h: -------------------------------------------------------------------------------- 1 | /* file abstraction */ 2 | 3 | #ifndef MEMF_INTERNAL_H 4 | #define MEMF_INTERNAL_H 5 | 6 | #include 7 | 8 | /* read / write function(file, buffer, count) */ 9 | typedef size_t rwfunc(void*, uint8_t*, size_t); 10 | 11 | /* read / write / ignore data (for nopf) */ 12 | enum direction {READ, WRITE}; 13 | 14 | /* 15 | * abstraction over a streaming data source / drain. The member dir 16 | * says whether we want to read from or write to the rwfile. The 17 | * function readwrite can be used to do either. It shall return the 18 | * number of bytes read / written; if this number is smaller than the 19 | * requested number of bytes, it is assumed that an error occured. 20 | */ 21 | struct rwfile { 22 | rwfunc *readwrite; 23 | void *file; 24 | enum direction dir; 25 | }; 26 | 27 | enum byteorder {MOTOROLA, INTEL}; 28 | enum {STCKSIZ = 15}; /* number of nesting levels allowed in parsefstr */ 29 | 30 | /* state of the fstr parser; zero value is state at beginning. */ 31 | struct pstate { 32 | size_t fidx; 33 | enum byteorder bo; 34 | int lvl; /* next free stack item */ 35 | 36 | /* nesting stack for () contexts */ 37 | struct { 38 | size_t fidx; /* index into fstr */ 39 | unsigned long rep; /* repetitions of the next item */ 40 | enum byteorder bo; /* byteorder to restore */ 41 | } stck[STCKSIZ]; 42 | }; 43 | 44 | 45 | extern size_t memf(struct rwfile*, const char*, void *restrict); 46 | extern int parsefstr(const char*, struct pstate*, int*, long*); 47 | 48 | #endif /* MEMF_INTERNAL_H */ 49 | -------------------------------------------------------------------------------- /src/memf.c: -------------------------------------------------------------------------------- 1 | /* generic memf function */ 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "memf-internal.h" 10 | #include "fiddle.h" 11 | 12 | enum { 13 | /* 14 | * Length of the buffer for marshalling objects. Must be larger than 15 | * sizeof (uint64_t). A buffer of this size is placed in automatic 16 | * storage (i.e. on the stack); reduce size on platforms with 17 | * constrainted memory. 18 | */ 19 | BUFLEN = 512, 20 | }; 21 | 22 | static size_t skip_data(struct rwfile*, long); 23 | static size_t move_data(struct rwfile*, char*, size_t*, int, long, 24 | enum byteorder); 25 | 26 | /* 27 | * Parse the next item of fstr with respect to the parser state described in 28 | * pst. Returns 0 on end or error condition, 1 otherwise. If an error occurs 29 | * during parsing, errno is set to EINVAL. After succesful parsing an item, 30 | * rep contains the number of times it shall be repeated and size contains the 31 | * number of bytes in the item (e.g. 8 for an uint64_t), or -1 for skipping. 32 | * Take the byte order of the next item from pst->bo. 33 | */ 34 | extern int 35 | parsefstr(const char *fstr, struct pstate *pst, int *size, long *rep) 36 | { 37 | char *endptr; 38 | 39 | begin: 40 | *rep = 1; 41 | 42 | rescan: 43 | switch (fstr[pst->fidx++]) { 44 | case '\0': 45 | if (pst->lvl != 0 || *rep != 1) 46 | errno = EINVAL; 47 | 48 | return 0; 49 | 50 | case ' ': /* skip blanks */ 51 | goto begin; 52 | 53 | case 'i': pst->bo = INTEL; goto begin; 54 | case 'm': pst->bo = MOTOROLA; goto begin; 55 | 56 | case 'z': *size = -1; return 1; 57 | case 'c': *size = 0; return 1; 58 | case 'h': *size = 1; return 1; 59 | case 'd': *size = 2; return 1; 60 | case 'l': *size = 3; return 1; 61 | 62 | case '1': 63 | case '2': 64 | case '3': 65 | case '4': 66 | case '5': 67 | case '6': 68 | case '7': 69 | case '8': 70 | case '9': 71 | *rep = strtol(fstr + pst->fidx - 1, &endptr, 10); 72 | if (*rep == LONG_MAX) 73 | return 0; /* errno == ERANGE */ 74 | 75 | pst->fidx = (size_t)(endptr - fstr); 76 | 77 | goto rescan; 78 | 79 | case '(': 80 | if (pst->lvl == STCKSIZ) { 81 | errno = ENOMEM; 82 | return 0; 83 | } 84 | 85 | pst->stck[pst->lvl].bo = pst->bo; 86 | pst->stck[pst->lvl].fidx = pst->fidx; 87 | pst->stck[pst->lvl].rep = *rep; 88 | pst->lvl++; 89 | goto begin; 90 | 91 | case ')': 92 | if (pst->lvl == 0) { 93 | errno = EINVAL; 94 | return 0; 95 | } 96 | 97 | pst->bo = pst->stck[pst->lvl - 1].bo; 98 | 99 | /* do the group again? */ 100 | if (pst->stck[pst->lvl - 1].rep-- > 1) 101 | pst->fidx = pst->stck[pst->lvl - 1].fidx; 102 | else 103 | pst->lvl--; 104 | 105 | goto begin; 106 | 107 | default: 108 | errno = EINVAL; 109 | return 0; 110 | } 111 | } 112 | 113 | /* 114 | * generic core of the memf family of functions. Operates on an abstract struct 115 | * rwfile with operation direction (read / write) specified by dir. Returns the 116 | * number of bytes read / written from / to file. If an error occurs, this count 117 | * is short. Use nopf() to figure out what memf() should return when it runs 118 | * successfully. 119 | */ 120 | extern size_t 121 | memf(struct rwfile *file, const char *fstr, void *restrict reprarg) 122 | { 123 | struct pstate pst; 124 | size_t count, total = 0, ridx = 0; 125 | long rep, chunk; 126 | int size; 127 | char *repr = reprarg; 128 | 129 | memset(&pst, 0, sizeof pst); 130 | 131 | while (parsefstr(fstr, &pst, &size, &rep)) { 132 | /* directive 'z' (skip) */ 133 | if (size < 0) { 134 | total += count = skip_data(file, rep); 135 | if (count < rep) 136 | return total; 137 | 138 | continue; 139 | } 140 | 141 | /* align ridx according to size */ 142 | switch (size) { 143 | case 0: ridx = alignto(structalign(uint8_t), ridx); break; 144 | case 1: ridx = alignto(structalign(uint16_t), ridx); break; 145 | case 2: ridx = alignto(structalign(uint32_t), ridx); break; 146 | case 3: ridx = alignto(structalign(uint64_t), ridx); break; 147 | } 148 | 149 | /* Process read / write in chunks of BUFLEN size */ 150 | for (; rep > 0; rep -= BUFLEN >> size) { 151 | chunk = rep > BUFLEN >> size ? BUFLEN >> size : rep; 152 | 153 | total += count = 154 | move_data(file, repr, &ridx, size, chunk, pst.bo); 155 | if (count < chunk << size) 156 | return total; 157 | } 158 | } 159 | 160 | return total; 161 | } 162 | 163 | /* 164 | * Move rep objects of type uint(1 << size)_t between file and repr. Assume 165 | * rep << size < BUFLEN. Advance ridx to point just past the last byte written 166 | * into repr. Return byte count from / to file; byte count is less than 167 | * rep << size on error. 168 | */ 169 | static size_t 170 | move_data(struct rwfile *file, char *repr, size_t *ridx, int size, long rep, 171 | enum byteorder bo) 172 | { 173 | size_t count; 174 | long i; 175 | uint8_t buf[BUFLEN]; 176 | 177 | if (file->dir == READ) { 178 | count = file->readwrite(file->file, buf, size << rep); 179 | if (count != size << rep) 180 | return count; 181 | 182 | for (i = 0; i < rep; i += 1 << size) 183 | switch (size) { 184 | #define RCASE(n) *(uint##n##_t*)(repr + *ridx + i) = \ 185 | bo == MOTOROLA ? rm##n(buf + i) : ri##n(buf + i) 186 | case 0: RCASE(8); break; 187 | case 1: RCASE(16); break; 188 | case 2: RCASE(32); break; 189 | case 3: RCASE(64); break; 190 | #undef RCASE 191 | } 192 | } else { 193 | for (i = 0; i < rep; i += 1 << size) 194 | switch(size) { 195 | #define WCASE(n) bo == MOTOROLA \ 196 | ? wm##n(buf + i, *(uint##n##_t*)(repr + *ridx + i)) \ 197 | : wi##n(buf + i, *(uint##n##_t*)(repr + *ridx + i)) 198 | case 0: WCASE(8); break; 199 | case 1: WCASE(16); break; 200 | case 2: WCASE(32); break; 201 | case 3: WCASE(64); break; 202 | #undef WCASE 203 | } 204 | 205 | count = file->readwrite(file->file, buf, size << rep); 206 | } 207 | 208 | *ridx += i; 209 | return count; 210 | } 211 | 212 | /* 213 | * Skip rep bytes of file: write 0 bytes if directions is WRITE, ignore what has 214 | * been read if direction is READ. Return number of bytes skipped, byte count is 215 | * less than rep on error. 216 | */ 217 | static size_t 218 | skip_data(struct rwfile *file, long rep) 219 | { 220 | size_t n, total = 0, count; 221 | uint8_t buf[BUFLEN]; 222 | 223 | memset(buf, 0, rep > BUFLEN ? BUFLEN : rep); 224 | 225 | for (; rep > 0; rep -= BUFLEN) { 226 | n = rep > BUFLEN ? BUFLEN : rep; 227 | 228 | total += count = file->readwrite(file->file, buf, n); 229 | if (count < n) 230 | return total; 231 | } 232 | 233 | return total; 234 | } 235 | -------------------------------------------------------------------------------- /src/memf.h: -------------------------------------------------------------------------------- 1 | /* formatted read into / write from binary structures */ 2 | 3 | #ifndef MEMF_H 4 | #define MEMF_H 5 | 6 | #include 7 | 8 | #ifdef __STDC_HOSTED__ 9 | # include 10 | extern size_t freadf(FILE*, const char*, void *restrict); 11 | extern size_t fwritef(FILE*, const char*, const void*); 12 | #endif 13 | 14 | extern size_t mreadf(const uint8_t *restrict, const char*, void *restrict); 15 | extern size_t mwritef(uint8_t *restrict, const char*, const void*); 16 | 17 | extern size_t fdwritef(int, const char*, const void*); 18 | extern size_t fdreadf(int, const char*, void *restrict); 19 | 20 | extern size_t sizef(const char*); 21 | extern size_t lenf(const char*); 22 | 23 | #endif /* MEMF_H */ 24 | -------------------------------------------------------------------------------- /src/mreadf.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "memf.h" 4 | #include "memf-internal.h" 5 | 6 | static rwfunc rwmread; 7 | 8 | /* 9 | * Read count bytes from file and then increment the place file points 10 | * to appropriately. 11 | */ 12 | static size_t 13 | rwmread(void *file, uint8_t *buf, size_t count) 14 | { 15 | uint8_t **source = (uint8_t**)file; 16 | 17 | memcpy(buf, *source, count); 18 | *source += count; 19 | 20 | return count; 21 | } 22 | 23 | /* 24 | * Read from source and marshall into repr. 25 | */ 26 | extern size_t 27 | mreadf(const uint8_t *source, const char *fstr, void *restrict repr) 28 | { 29 | struct rwfile rwf = {&rwmread, &source, READ}; 30 | 31 | return memf(&rwf, fstr, repr); 32 | } 33 | -------------------------------------------------------------------------------- /src/mwritef.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "memf.h" 4 | #include "memf-internal.h" 5 | 6 | static rwfunc rwmwrite; 7 | 8 | /* 9 | * Write count bytes into file and then increment the place file points 10 | * to appropriately. 11 | */ 12 | static size_t 13 | rwmwrite(void *file, uint8_t *buf, size_t count) 14 | { 15 | uint8_t **dest = (uint8_t**)file; 16 | 17 | memcpy(*dest, buf, count); 18 | *dest += count; 19 | 20 | return count; 21 | } 22 | 23 | /* 24 | * Marshall from repr into dest. 25 | */ 26 | extern size_t 27 | mwritef(uint8_t *dest, const char *fstr, const void *repr) 28 | { 29 | struct rwfile rwf = {&rwmwrite, &dest, WRITE}; 30 | 31 | return memf(&rwf, fstr, (void*)repr); 32 | } 33 | -------------------------------------------------------------------------------- /src/sizef.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "fiddle.h" 4 | #include "memf.h" 5 | #include "memf-internal.h" 6 | 7 | /* 8 | * Compute the size of the structure representation as described by fstr 9 | * If the formatting string is invalid, errno will be set to a nonzero 10 | * value and the value returned is invalid. You can use this function to 11 | * validate the formatting string. If you call mreadf(buf, fstr, &str), 12 | * then msizef(fstr) should not be larger than sizeof str. It might be 13 | * slightly smaller because of padding added to the end of str. 14 | */ 15 | extern size_t 16 | msizef(const char *fstr) 17 | { 18 | struct pstate pst; 19 | size_t ridx = 0; 20 | long rep; 21 | int size; 22 | 23 | memset(&pst, 0, sizeof pst); 24 | 25 | while (parsefstr(fstr, &pst, &size, &rep)) { 26 | switch (size) { 27 | case 0: ridx = alignto(structalign(uint8_t), ridx); break; 28 | case 1: ridx = alignto(structalign(uint16_t), ridx); break; 29 | case 2: ridx = alignto(structalign(uint32_t), ridx); break; 30 | case 3: ridx = alignto(structalign(uint64_t), ridx); break; 31 | default: continue; /* specifier 'z' */ 32 | } 33 | 34 | ridx += rep << size; 35 | } 36 | 37 | return ridx; 38 | } 39 | -------------------------------------------------------------------------------- /test/Makefile: -------------------------------------------------------------------------------- 1 | CC=c99 2 | CFLAGS=-O2 -L../src/ -I../src/ 3 | TESTS=parsertest 4 | 5 | test: parsertest 6 | @./parsertest 7 | 8 | .o: 9 | $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< -lmemf 10 | 11 | .c: 12 | $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< -lmemf 13 | 14 | clean: 15 | rm -f *.o $(TESTS) 16 | 17 | .PHONY: test clean 18 | -------------------------------------------------------------------------------- /test/mem_skel.c: -------------------------------------------------------------------------------- 1 | /* 2 | * skeleton for the tests generated by memtestgen. This file is included by the 3 | * generated source file of each test case. The generated files only contain 4 | * the definition of struct testcase, the functions prototyped below and the 5 | * constant TESTFSTR. This file contains the code that calls the memf() 6 | * functions and validates them on the generated structure and test cases. 7 | */ 8 | 9 | #include 10 | #include 11 | #include 12 | #include "memf.h" 13 | 14 | /* defined by the includer */ 15 | struct testcase; 16 | 17 | static size_t testsize(void); /* returns sizeof (struct testcase) */ 18 | static size_t testlen(void); /* should be equal to lenf(TESTFSTR) */ 19 | static void filltest(struct testcase*); /* fill testcase with testing pattern */ 20 | static void cleartest(struct testcase*); /* set each member to 0 */ 21 | static int iscorrect(const struct testcase*); /* check against testing pattern */ 22 | 23 | /* auxillary functions */ 24 | static void pass(const char*, ...); 25 | static void fail(const char*, ...); 26 | 27 | /* each test has this format */ 28 | typedef void testcase(void); 29 | 30 | static testcase test1, test2; 31 | 32 | /* test 1: Is testlen() == lenf()? */ 33 | static int 34 | test1(void) 35 | { 36 | size_t tl_expect, tl_is; 37 | 38 | tl_expect = testlen(); 39 | tl_is = lenf(TESTFSTR); 40 | 41 | if (tl_expect == tl_is) 42 | pass("%d == %d", tl_expect, tl_is); 43 | else 44 | fail("%d != %d", tl_expect, tl_is); 45 | } 46 | 47 | /* test 2: Is testsize() >= sizef()? */ 48 | static int 49 | test1(void) 50 | { 51 | size_t ts_expect, ts_is; 52 | 53 | ts_expect = testsize(); 54 | ts_is = sizef(TESTFSTR); 55 | 56 | if (ts_expect >= ts_is) 57 | pass("%d >= %d", ts_expect, ts_is); 58 | else 59 | fail("%d < %d", ts_expect, ts_is); 60 | } 61 | 62 | 63 | -------------------------------------------------------------------------------- /test/parsertest.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Verify that parsefstr() from memf.c parses formatting strings correctly. 3 | */ 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "memf-internal.h" 9 | 10 | struct item { 11 | long rep; 12 | int size; 13 | enum byteorder bo; 14 | }; 15 | 16 | struct testcase { 17 | const char *fstr; 18 | size_t itemcount; 19 | int errval; /* value of errno. If errval != 0, itemcount should be 0 */ 20 | 21 | const struct item *items; 22 | }; 23 | 24 | enum sizes { 25 | Z = -1, 26 | C = 0, 27 | H = 1, 28 | D = 2, 29 | L = 3 30 | }; 31 | 32 | #define ITEM (const struct item[]) 33 | const struct testcase testcases[] = { 34 | ")", 0, EINVAL, NULL, 35 | "23", 0, EINVAL, NULL, 36 | "(", 0, EINVAL, NULL, 37 | "()", 0, 0, NULL, 38 | "1(2(3(4())))", 0, 0, NULL, 39 | "1(2(3(4()))", 0, EINVAL, NULL, 40 | "1(2(3(4()))))",0, EINVAL, NULL, 41 | "?", 0, EINVAL, NULL, 42 | "999999999999999999999999999999c", 0, ERANGE, NULL, 43 | "d", 1, 0, ITEM{1, D, MOTOROLA}, 44 | "ic", 1, 0, ITEM{1, C, INTEL}, 45 | "i2(cd)", 4, 0, ITEM{1, C, INTEL, 1, D, INTEL, 1, C, INTEL, 1, D, INTEL}, 46 | "i2((c)d)", 4, 0, ITEM{1, C, INTEL, 1, D, INTEL, 1, C, INTEL, 1, D, INTEL}, 47 | "i2(zmh)l", 5, 0, ITEM{1, Z, INTEL, 1, H, MOTOROLA, 1, Z, INTEL, 1, H, MOTOROLA, 1, L, INTEL}, 48 | "2(c3d4h)2z", 7, 0, ITEM{1, C, MOTOROLA, 3, D, MOTOROLA, 4, H, MOTOROLA, 1, C, MOTOROLA, 3, D, MOTOROLA, 4, H, MOTOROLA, 2, Z, MOTOROLA}, 49 | "2 3c4d 5z", 3, 0, ITEM{3, C, MOTOROLA, 4, D, MOTOROLA, 5, Z, MOTOROLA}, 50 | "i3c8chchchhc3h2d", 11, 0, ITEM{3, C, INTEL, 8, C, INTEL, 1, H, INTEL, 1, C, INTEL, 1, H, INTEL, 1, C, INTEL, 1, H, INTEL, 1, H, INTEL, 1, C, INTEL, 3, H, INTEL, 2, D, INTEL}, 51 | "m5l3(7zid2(cmh)l)l",23,0, ITEM{5, L, MOTOROLA, 7, Z, MOTOROLA, 1, D, INTEL, 1, C, INTEL, 1, H, MOTOROLA, 1, C, INTEL, 1, H, MOTOROLA, 1, L, INTEL, 7, Z, MOTOROLA, 1, D, INTEL, 1, C, INTEL, 1, H, MOTOROLA, 1, C, INTEL, 1, H, MOTOROLA, 1, L, INTEL, 7, Z, MOTOROLA, 1, D, INTEL, 1, C, INTEL, 1, H, MOTOROLA, 1, C, INTEL, 1, H, MOTOROLA, 1, L, INTEL, 1, L, MOTOROLA} 52 | }; 53 | #undef ITEM 54 | 55 | static int fails(const struct testcase*); 56 | 57 | extern int 58 | main() 59 | { 60 | size_t i; 61 | int fail = 0; 62 | 63 | printf("testing parser... "); 64 | fflush(stdout); 65 | 66 | for (i = 0; i < sizeof testcases / sizeof *testcases; i++) 67 | if (fails(testcases + i)) { 68 | if (fail == 0) 69 | puts(""); 70 | 71 | fail = 1; 72 | printf("parser test %zu FAILED\n", i); 73 | } 74 | 75 | if (!fail) 76 | puts("PASS"); 77 | 78 | return fail; 79 | } 80 | 81 | /* return if test c fails */ 82 | static int 83 | fails(const struct testcase *c) 84 | { 85 | struct pstate pst; 86 | size_t i = 0; 87 | long rep; 88 | int size, failed = 0; 89 | 90 | memset(&pst, 0, sizeof pst); 91 | errno = 0; 92 | 93 | for (; parsefstr(c->fstr, &pst, &size, &rep); i++) 94 | if (i >= c->itemcount || rep != c->items[i].rep || 95 | size != c->items[i].size || pst.bo != c->items[i].bo) 96 | failed = 1; 97 | 98 | /* ex falso quodlibet */ 99 | if (errno != 0) 100 | return errno != c->errval; 101 | 102 | if (i != c->itemcount) 103 | return 1; 104 | 105 | return failed; 106 | } 107 | -------------------------------------------------------------------------------- /test/ptestgen.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Utility to generate test cases for parsertest.c. This utility uses the parser 3 | * itself to generate test cases. Please make sure that the test case behaves 4 | * correctly before adding it. 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include "memf-internal.h" 11 | 12 | extern int 13 | main(int argc, char *argv[]) 14 | { 15 | struct pstate pst; 16 | size_t i = 0, len; 17 | long rep; 18 | int size, failed = 0; 19 | 20 | if (argc != 2) { 21 | fprintf(stderr, "Usage: %s fstr\n", argv[0]); 22 | return 1; 23 | } 24 | 25 | printf("\"%s\", ", argv[1]); 26 | 27 | /* first, try to figure out how many items fstr contains */ 28 | memset(&pst, 0, sizeof pst); 29 | len = errno = 0; 30 | while (parsefstr(argv[1], &pst, &size, &rep)) 31 | len++; 32 | 33 | if (errno != 0) { 34 | /* don't care about item count if an error occured */ 35 | printf("0, %d, NULL,\n", errno); 36 | return 0; 37 | } 38 | 39 | printf("%zu, 0, ITEM{", len); 40 | 41 | /* now populate items */ 42 | memset(&pst, 0, sizeof pst); 43 | while (parsefstr(argv[1], &pst, &size, &rep)) 44 | printf("%ld, %c, %s, ", rep, "ZCHDL"[size + 1], 45 | pst.bo == MOTOROLA ? "MOTOROLA" : "INTEL"); 46 | 47 | printf("},\n"); 48 | 49 | return 0; 50 | } 51 | --------------------------------------------------------------------------------