Skip to content

Commit cab5548

Browse files
Shallow copy of npy_array_t (#31)
* add interface to build npy_array_list objects from C * rename to deepcopy * deepcopy and shallow copy * fix readme * add copying example to workflow * left a note in copying.c for having the same data as copying_read.c --------- Co-authored-by: alcubierre-drive <>
1 parent 80a0b2c commit cab5548

File tree

7 files changed

+129
-15
lines changed

7 files changed

+129
-15
lines changed

.github/workflows/make_and_exmples.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,9 @@ jobs:
3333
./how_to_save_npz
3434
./how_to_load my_4_by_3_array_shortcut.npy
3535
./how_to_load_npz iarray_and_darray.npz
36-
- name: Test install
36+
./copying
37+
./copying_read
38+
- name: Test install
3739
run: |
3840
sudo make install
3941
unset LD_LIBRARY_PATH

README.md

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -119,12 +119,13 @@ And the linked list structure for `.npz` files:
119119
} npy_array_list_t;
120120

121121
## API
122-
The API is really simple. There is only ~~eleven~~twelve public functions:
122+
The API is really simple. There is only 13 public functions:
123123

124124
/* These are the four functions for loading and saving .npy files */
125125
npy_array_t* npy_array_load ( const char *filename);
126126
npy_array_t* npy_array_mmap ( const char *filename);
127-
npy_array_t* npy_array_alloc ( const npy_array_t *m );
127+
npy_array_t* npy_array_deepcopy ( const npy_array_t *m );
128+
npy_array_t* npy_array_copy ( const npy_array_t *m );
128129
void npy_array_dump ( const npy_array_t *m );
129130
void npy_array_save ( const char *filename, const npy_array_t *m );
130131
void npy_array_free ( npy_array_t *m );
@@ -167,13 +168,22 @@ compile this with:
167168
You can the run example_list with a filename (_NumPy_ compressed) as argument.
168169

169170
#include "npy_array_list.h"
171+
170172
int main(int argc, char *argv[])
171173
{
172174
if( argc != 2 ) return -1;
175+
173176
double data[] = {0,1,2,3,4,5};
177+
174178
npy_array_list_t* list = NULL;
175-
list = npy_array_list_append( list, NPY_ARRAY_ALLOCATOR(data, SHAPE(3,2), NPY_DTYPE_FLOAT64), "matrix" );
176-
list = npy_array_list_append( list, NPY_ARRAY_ALLOCATOR(data, SHAPE(2,1,2), NPY_DTYPE_FLOAT64), "tensor" );
179+
180+
// the first npy_array_t* holds a reference to the data array
181+
list = npy_array_list_append( list,
182+
NPY_ARRAY_BUILDER_COPY(data, SHAPE(3,2), NPY_DTYPE_FLOAT64), "matrix" );
183+
// the second npy_array_t* holds a copy of the data array (hence DEEPCOPY)
184+
list = npy_array_list_append( list,
185+
NPY_ARRAY_BUILDER_DEEPCOPY(data, SHAPE(2,1,2), NPY_DTYPE_FLOAT64), "tensor" );
186+
177187
npy_array_list_save_compressed( argv[1], list, ZIP_CM_DEFAULT, 0 );
178188
npy_array_list_free( list );
179189
}

example/Makefile

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
CC = gcc
2-
CFLAGS = -std=c99 -Wall -Wextra -O3 -I..
3-
LDLIBS = -L.. -lnpy_array
1+
CC ?= gcc
2+
CFLAGS += -std=c99 -Wall -Wextra -O3 -I..
3+
LDLIBS += -L.. -lnpy_array
44

55
SOURCES = $(wildcard *.c)
6-
PROGRAMS = $(patsubst %.c,%,$(SOURCES))
6+
PROGRAMS = $(patsubst %.c,%,$(SOURCES))
77

88
all: $(PROGRAMS)
99

example/copying.c

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
#include "npy_array_list.h"
2+
3+
// the first block must be the same as in copying_read.c
4+
int main(int argc, char *argv[])
5+
{
6+
const char* fname = (argc != 2) ? "copy.npz" : argv[1];
7+
8+
const double data[] = {0,1,2, 3,4,5};
9+
const int32_t idata[] = {0,1, 2,3,
10+
4,5, 6,7,
11+
8,9, 10,11};
12+
const char names[][8] = { "double", "int" };
13+
14+
npy_array_list_t* list = NULL;
15+
16+
// the first npy_array_t* holds a reference to the data array
17+
list = npy_array_list_append( list,
18+
NPY_ARRAY_BUILDER_COPY(data, SHAPE(2,3), NPY_DTYPE_FLOAT64), names[0] );
19+
// the second npy_array_t* holds a copy of the data array (hence DEEPCOPY)
20+
list = npy_array_list_append( list,
21+
NPY_ARRAY_BUILDER_DEEPCOPY(idata, SHAPE(3,2,2), NPY_DTYPE_INT32), names[1] );
22+
23+
npy_array_list_save_compressed( fname, list, ZIP_CM_DEFAULT, 0 );
24+
npy_array_list_free( list );
25+
}

example/copying_read.c

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
#include "npy_array_list.h"
2+
3+
#include <string.h>
4+
#define MIN(x,y) ((x)<(y)?(x):(y))
5+
6+
// the first block must be the same as in copying.c
7+
int main(int argc, char *argv[])
8+
{
9+
const char* fname = (argc != 2) ? "copy.npz" : argv[1];
10+
11+
const double data[] = {0,1,2, 3,4,5};
12+
const int32_t idata[] = {0,1, 2,3,
13+
4,5, 6,7,
14+
8,9, 10,11};
15+
const char names[][8] = { "double", "int" };
16+
17+
// we load the file and check whether it contains the same data
18+
npy_array_list_t *list = npy_array_list_load( fname );
19+
20+
int errors = 0;
21+
int visited_data = 0,
22+
visited_idata = 0;
23+
npy_array_list_t *elem = list;
24+
while (elem != NULL) {
25+
npy_array_t* ary = elem->array;
26+
size_t ary_sz = npy_array_calculate_datasize( ary );
27+
28+
char* cmp = NULL;
29+
size_t cmp_sz = 0;
30+
31+
if (!strcmp( elem->filename, names[0] )) {
32+
cmp = (char*)data;
33+
cmp_sz = sizeof(data);
34+
visited_data++;
35+
} else if (!strcmp( elem->filename, names[1] )) {
36+
cmp = (char*)idata;
37+
cmp_sz = sizeof(idata);
38+
visited_idata++;
39+
}
40+
41+
errors += (cmp_sz != ary_sz);
42+
if (cmp != NULL) {
43+
errors += (memcmp( ary->data, cmp, MIN(cmp_sz,ary_sz) ) != 0);
44+
} else {
45+
errors++;
46+
}
47+
48+
elem = elem->next;
49+
}
50+
51+
errors += (visited_data != 1);
52+
errors += (visited_idata != 1);
53+
54+
npy_array_list_free( list );
55+
56+
// we return the number of errors/inconsistencies
57+
return errors;
58+
}

npy_array.c

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -257,7 +257,7 @@ npy_array_t * _read_matrix( void *fp, reader_func read_func )
257257
return m;
258258
}
259259

260-
m->data = malloc( n_elements * m->elem_size );
260+
m->memory = m->data = malloc( n_elements * m->elem_size );
261261
if ( !m->data ){
262262
fprintf(stderr, "Cannot allocate memory for matrix data.\n");
263263
free( m );
@@ -392,8 +392,9 @@ void npy_array_free( npy_array_t *m )
392392

393393
size_t len = NPY_ARRAY_PREHEADER_LENGTH + header_length + npy_array_calculate_datasize(m);
394394
munmap( m->map_addr, len );
395-
} else
396-
free( m->data );
395+
} else if ( m->memory ) {
396+
free( m->memory );
397+
}
397398

398399
free( m );
399400
}
@@ -409,7 +410,7 @@ npy_array_t* npy_array_deepcopy( const npy_array_t* m ) {
409410
memcpy( ary->shape, m->shape, sizeof(ary->shape) );
410411
ary->typechar = m->typechar;
411412
ary->elem_size = m->elem_size;
412-
ary->data = malloc( npy_array_calculate_datasize(ary) );
413+
ary->memory = ary->data = malloc( npy_array_calculate_datasize(ary) );
413414
if (!ary->data) {
414415
fprintf(stderr, "Cannot allocate memory!\n");
415416
free(ary);
@@ -418,3 +419,17 @@ npy_array_t* npy_array_deepcopy( const npy_array_t* m ) {
418419
memcpy( ary->data, m->data, npy_array_calculate_datasize(ary) );
419420
return ary;
420421
}
422+
423+
npy_array_t* npy_array_copy( const npy_array_t* m ) {
424+
npy_array_t* ary = calloc( 1, sizeof(*ary) );
425+
if (!ary) {
426+
fprintf(stderr, "Cannot allocate data structure!\n");
427+
return NULL;
428+
}
429+
ary->ndim = MIN( m->ndim, NPY_ARRAY_MAX_DIMENSIONS );
430+
memcpy( ary->shape, m->shape, sizeof(ary->shape) );
431+
ary->typechar = m->typechar;
432+
ary->elem_size = m->elem_size;
433+
ary->data = m->data;
434+
return ary;
435+
}

npy_array.h

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,14 +53,16 @@ typedef struct _npy_array_t {
5353
char typechar;
5454
size_t elem_size;
5555
bool fortran_order;
56-
/* Consider map_addr as a private member. Do modify this pointer! Used for unmap() */
56+
/* Consider map_addr and memory as a private members. Do not modify these pointers! */
5757
void *map_addr; /* pointer to the map if array is mmap()'ed -- else NULL */
58+
char *memory; /* pointer to memory if array owns it -- else NULL */
5859
} npy_array_t;
5960

6061

6162
npy_array_t* npy_array_load ( const char *filename );
6263
npy_array_t* npy_array_mmap ( const char *filename );
6364
npy_array_t* npy_array_deepcopy ( const npy_array_t *m );
65+
npy_array_t* npy_array_copy ( const npy_array_t *m );
6466
void npy_array_dump ( const npy_array_t *m );
6567
void npy_array_save ( const char *filename, const npy_array_t *m );
6668
void npy_array_free ( npy_array_t *m );
@@ -87,7 +89,9 @@ npy_array_t * _read_matrix( void *fp, reader_func read_func );
8789
#define SHAPE(...) .shape = {__VA_ARGS__}, .ndim = _NARG(__VA_ARGS__)
8890
#define NPY_ARRAY_BUILDER(_data,_shape,...) \
8991
&(npy_array_t){ .data=(char*)_data, _shape, __VA_ARGS__ }
90-
#define NPY_ARRAY_ALLOCATOR(...) \
92+
#define NPY_ARRAY_BUILDER_COPY(...) \
93+
npy_array_copy(NPY_ARRAY_BUILDER(__VA_ARGS__))
94+
#define NPY_ARRAY_BUILDER_DEEPCOPY(...) \
9195
npy_array_deepcopy(NPY_ARRAY_BUILDER(__VA_ARGS__))
9296

9397
#define NPY_DTYPE_FLOAT16 .typechar='f', .elem_size=2

0 commit comments

Comments
 (0)