1 /** \file \internal
2 Basic NC_INMEMORY API tests both for netcdf-3 and netcdf-4
3 
4 Copyright 2018, UCAR/Unidata. See COPYRIGHT file for copying and
5 redistribution conditions.
6 */
7 
8 #undef DDBG
9 
10 #include "config.h"
11 #include <stdio.h>
12 #include <stdlib.h>
13 #ifdef HAVE_UNISTD_H
14 #include <unistd.h>
15 #endif
16 
17 #include "netcdf.h"
18 #include "netcdf_mem.h"
19 #include "ncbytes.h"
20 #include "nc_tests.h"
21 #include "err_macros.h"
22 
23 #ifdef USE_HDF5
24 #include <hdf5.h>
25 extern int H5Eprint1(FILE * stream);
26 #endif
27 
28 #define FLAGS4 (NC_INMEMORY|NC_NETCDF4|NC_CLOBBER)
29 #define FLAGS3 (NC_INMEMORY|NCCLOBBER)
30 
31 #define NC_NETCDF3 0
32 #define MODIFIED 1
33 #define EXTRA 1
34 
35 #define LARGE_SPACE (1<<18)
36 
37 #define FILE3 "tst_inmemory3.nc"
38 #define FILE4 "tst_inmemory4.nc"
39 #define CREATE3 "tst_inmemory3_create.nc"
40 #define CREATE4 "tst_inmemory4_create.nc"
41 #define XFAIL "tst_xfail.nc"
42 #define MISC "tst_misc.nc"
43 
44 #define CREATEFILE3 "tst_memcreate3.nc"
45 #define CREATEFILE4 "tst_memcreate4.nc"
46 
47 /* Make no dimension larger than this */
48 
49 #define MAXDIMLEN (1024*12)
50 #define MAXDIMS 3 /* for defining arrays; includes all dimensions */
51 #define MAXVARS 4 /* for defining arrays; includes all variables  */
52 
53 #define NDIMS0 2 /* # dimensions in define_metadata: 1 unlimited + 1 fixed */
54 #define DIM0_NAME "fun" /* unlimited */
55 #define DIM0_LEN 2
56 
57 #define DIM1_NAME "money"
58 #define DIM1_LEN 400
59 #define ATT0_NAME "home"
60 #define ATT0_TEXT "earthship"
61 
62 #define NVARS0 3 /* # variables in define_metadata */
63 #define VAR0_NAME "nightlife"
64 #define VAR1_NAME "taxi_distance"
65 #define VAR2_NAME "time"
66 
67 /* Variable added by modify_file */
68 #define VAR3_NAME "miles"
69 
70 /* Added variable info for modify_file_extra */
71 #define DIMX_NAME "bigmoney"
72 #define DIMX_LEN 8000
73 #define VARX_NAME "expenses"
74 
75 #define FLOATVAL ((float)42.22)
76 
77 /*
78 CDL for created file:
79 netcdf tst_inmemory3 {
80 dimensions:
81 	fun = UNLIMITED ; // (2 currently)
82 	money = 8 ;
83 variables:
84 	int nightlife(fun, money) ;
85 	float time ;
86 	short taxi_distance(money) ;
87 
88 // global attributes:
89 		:home = "earthship" ;
90 data:
91 
92  nightlife =
93   0, 100, 200, 300, 400, 500, 600, 700,
94   800, 900, 1000, 1100, 1200, 1300, 1400, 1500 ;
95 
96  time = 42.22 ;
97 
98  taxi_distance = 0, 1, 2, 3, 4, 5, 6, 7 ;
99 }
100 */
101 
102 #ifdef DDBG
103 #undef ERR
104 static void
fail(int line)105 fail(int line) {
106     fflush(stdout);
107     fprintf(stderr,"\nline=%d\n",line);
108     fflush(stderr);
109     exit(1);
110 }
111 #define ERR fail(__LINE__)
112 #endif
113 
114 static int
fail(int stat,const char * file,int line,int xfail)115 fail(int stat, const char* file, int line, int xfail)
116 {
117     fflush(stdout);
118     fprintf(stderr,"***%sFail: line: %d; status=%d %s\n",
119 	            (xfail?"X":""),line,stat,nc_strerror(stat));
120     err++;
121     fflush(stderr);
122     return stat;
123 }
124 
125 static int
xxfail(int stat,const char * file,int line,int xfail)126 xxfail(int stat, const char* file, int line, int xfail)
127 {
128     fflush(stdout);
129     if(stat == NC_NOERR)
130         return fail(NC_EINVAL,file,line,xfail);
131     fprintf(stderr,"\t***XFail Pass: status=%d %s\n", stat,nc_strerror(stat));
132     stat = NC_NOERR; /* because xfail */
133     fflush(stderr);
134     return stat;
135 }
136 
137 static int
check(int stat,const char * file,int line,int xfail)138 check(int stat, const char* file, int line, int xfail)
139 {
140     if(!xfail && stat != NC_NOERR)
141     	return fail(stat,file,line,xfail);
142     if(xfail)
143     	return xxfail(stat,file,line,xfail);
144     return stat;
145 }
146 
147 #define CHECK(expr) {stat = check((expr),__FILE__,__LINE__,0); if(stat) return stat;}
148 #define XCHECK(expr) {stat = check((expr),__FILE__,__LINE__,1); if(stat) return stat;}
149 
150 #define REPORT(xfail,expr) {if((xfail)) {XCHECK((expr));} else {CHECK((expr));}}
151 
152 /**************************************************/
153 
154 static void
removefile(const char * path)155 removefile(const char* path)
156 {
157     unlink(path);
158 }
159 
160 static int
readfile(const char * path,NC_memio * memio)161 readfile(const char* path, NC_memio* memio)
162 {
163     int status = NC_NOERR;
164     FILE* f = NULL;
165     size_t filesize = 0;
166     size_t count = 0;
167     char* memory = NULL;
168     char* p = NULL;
169 
170     /* Open the file for reading */
171 #ifdef _MSC_VER
172     f = fopen(path,"rb");
173 #else
174     f = fopen(path,"r");
175 #endif
176     if(f == NULL)
177 	{status = errno; goto done;}
178     /* get current filesize */
179     if(fseek(f,0,SEEK_END) < 0)
180 	{status = errno; goto done;}
181     filesize = (size_t)ftell(f);
182     /* allocate memory */
183     memory = malloc((size_t)filesize);
184     if(memory == NULL)
185 	{status = NC_ENOMEM; goto done;}
186     /* move pointer back to beginning of file */
187     rewind(f);
188     count = filesize;
189     p = memory;
190     while(count > 0) {
191         size_t actual;
192         actual = fread(p,1,count,f);
193 	if(actual == 0 || ferror(f))
194 	    {status = NC_EIO; goto done;}
195 	count -= actual;
196 	p += actual;
197     }
198     if(memio) {
199 	memio->size = (size_t)filesize;
200 	memio->memory = memory;
201     }
202 done:
203     if(status != NC_NOERR && memory != NULL)
204 	free(memory);
205     if(f != NULL) fclose(f);
206     return status;
207 }
208 
209 
210 static int
writefile(const char * path,NC_memio * memio)211 writefile(const char* path, NC_memio* memio)
212 {
213     int status = NC_NOERR;
214     FILE* f = NULL;
215     size_t count = 0;
216     char* p = NULL;
217 
218     /* Open the file for writing */
219 #ifdef _MSC_VER
220     f = fopen(path,"wb");
221 #else
222     f = fopen(path,"w");
223 #endif
224     if(f == NULL)
225 	{status = errno; goto done;}
226     count = memio->size;
227     p = memio->memory;
228     while(count > 0) {
229         size_t actual;
230         actual = fwrite(p,1,count,f);
231 	if(actual == 0 || ferror(f))
232 	    {status = NC_EIO; goto done;}
233 	count -= actual;
234 	p += actual;
235     }
236 done:
237     if(f != NULL) fclose(f);
238     return status;
239 }
240 
241 
242 /* Duplicate an NC_memio instance; needed to avoid
243    attempting to use memory that might have been realloc'd
244    Allow the new memory to be larger than the src memory
245 */
246 int
duplicatememory(NC_memio * src,NC_memio * target,size_t alloc,void ** original)247 duplicatememory(NC_memio* src, NC_memio* target, size_t alloc, void** original)
248 {
249     if(src == NULL || target == NULL || src->size == 0 || src->memory == NULL)
250 	return NC_EINVAL;
251     *target = *src;
252     if(alloc == 0) alloc = src->size;
253     target->memory = malloc(alloc);
254     if(target->memory == NULL)
255 	return NC_ENOMEM;
256     if(original) *original = target->memory;
257     memcpy(target->memory,src->memory,src->size);
258     target->size = alloc;
259     return NC_NOERR;
260 }
261 
262 /*
263 Given an ncid of a created file, fill in the meta-data
264 and data as described by the above CDL. Do not close
265 the file.
266 */
267 
268 static int
define_metadata(int ncid)269 define_metadata(int ncid)
270 {
271     int stat = NC_NOERR;
272     int dimid[MAXDIMS], varid0, varid1, varid2;
273     size_t start[1] = {0};
274     size_t count[1] = {DIM1_LEN};
275     int dimprod = (DIM0_LEN*DIM1_LEN);
276     int i;
277     float float_data;
278     int nightdata[DIM0_LEN*DIM1_LEN] ;
279     short taxi_distance[DIM1_LEN] ;
280 
281     /* Create data to write */
282     float_data = FLOATVAL;
283 
284     for (i = 0; i < DIM1_LEN; i++)
285         taxi_distance[i] = i;
286 
287     for (i = 0; i < dimprod; i++)
288         nightdata[i] = (100*i);
289 
290     CHECK(nc_put_att_text(ncid, NC_GLOBAL, ATT0_NAME,
291 			sizeof(ATT0_TEXT), ATT0_TEXT));
292 
293     CHECK(nc_def_dim(ncid, DIM0_NAME, NC_UNLIMITED, &dimid[0]));
294     CHECK(nc_def_dim(ncid, DIM1_NAME, DIM1_LEN, &dimid[1]));
295 
296     CHECK(nc_def_var(ncid, VAR0_NAME, NC_INT, 2, dimid, &varid0));
297     CHECK(nc_def_var(ncid, VAR1_NAME, NC_SHORT, 1, &dimid[1], &varid1));
298     CHECK(nc_def_var(ncid, VAR2_NAME, NC_FLOAT, 0, NULL, &varid2));
299 
300     CHECK(nc_enddef(ncid));
301 
302     CHECK(nc_put_vara_short(ncid, varid1, start, count, taxi_distance));
303     CHECK(nc_put_var_float(ncid, varid2, &float_data));
304 
305     {
306         size_t start[2] = {0,0};
307         size_t count[2] = {2,DIM1_LEN};
308         CHECK(nc_put_vara_int(ncid, varid0, start, count, nightdata));
309     }
310 
311     return stat;
312 }
313 
314 
315 /*
316 Create our reference file as a real on-disk file
317 and read it back into memory
318 */
319 
320 static int
create_reference_file(const char * filename,int mode,NC_memio * filedata)321 create_reference_file(const char* filename, int mode, NC_memio* filedata)
322 {
323     int stat = NC_NOERR;
324     int ncid;
325 
326     CHECK(nc_create(filename, mode|NC_CLOBBER, &ncid)); /* overwrite */
327     CHECK(define_metadata(ncid));
328     CHECK(nc_close(ncid));
329 
330     /* Read back the contents of the file into memory */
331     if(filedata != NULL) {
332 	memset(filedata,0,sizeof(NC_memio));
333 	CHECK(readfile(filename,filedata));
334     }
335     return stat;
336 }
337 
338 static int
modify_file(int ncid)339 modify_file(int ncid)
340 {
341     int stat = NC_NOERR;
342     size_t i;
343     int varid3;
344     int dimid[1];
345     size_t len;
346     int data[MAXDIMLEN];
347 
348     /* Get id of the unlimited dimension */
349     if((stat=nc_inq_dimid(ncid, DIM0_NAME, dimid))) goto done;
350     /* get current dim length */
351     if((stat=nc_inq_dimlen(ncid, dimid[0], &len))) goto done;
352     /* open file for new meta-data */
353     if((stat=nc_redef(ncid))) goto done;
354     /* Define a new variable */
355     if((stat=nc_def_var(ncid, VAR3_NAME, NC_INT, 1, dimid, &varid3))) goto done;
356     /* close metadata */
357     if((stat=nc_enddef(ncid))) goto done;
358     /* Write data to new variable */
359     for(i=0;i<len;i++)
360 	data[i] = i;
361     if((stat=nc_put_var_int(ncid,varid3,data))) goto done;
362 done:
363     return stat;
364 }
365 
366 /* Use this to force significant file size increase */
367 static int
modify_file_extra(int ncid)368 modify_file_extra(int ncid)
369 {
370     int stat = NC_NOERR;
371     size_t i;
372     int varidx;
373     int dimid[1];
374     int data[MAXDIMLEN];
375 
376     /* open file for new meta-data */
377     if((stat=nc_redef(ncid))) goto done;
378     /* Define a new dimension */
379     CHECK(nc_def_dim(ncid, DIMX_NAME, DIMX_LEN, &dimid[0]));
380     /* Define a new variable using new dimension */
381     if((stat=nc_def_var(ncid, VARX_NAME, NC_INT, 1, dimid, &varidx))) goto done;
382     /* close metadata */
383     if((stat=nc_enddef(ncid))) goto done;
384     /* Write data to new variable */
385     for(i=0;i<DIMX_LEN;i++)
386 	data[i] = i;
387     if((stat=nc_put_var_int(ncid,varidx,data))) goto done;
388 done:
389     return stat;
390 }
391 
392 /* Verify the content of a file */
393 static int
verify_file(int ncid,int modified,int extra)394 verify_file(int ncid, int modified, int extra)
395 {
396     int stat = NC_NOERR;
397     int i;
398     int dimid_in[MAXDIMS];
399     int dimid[MAXDIMS];
400     int ndims_in, nvars_in, natts_in, unlimdimid_in;
401     char name_in[NC_MAX_NAME + 1], att0_in[NC_MAX_NAME + 1];
402     nc_type type_in;
403     size_t len_in;
404     int varid[5];
405     int nightdata_in[DIM0_LEN*DIM1_LEN] ;
406     float float_data_in;
407     int milesdata_in[MAXDIMLEN];
408     int expenses_in[MAXDIMLEN];
409     short taxi_distance_in[MAXDIMLEN];
410     int dimprod = DIM0_LEN * DIM1_LEN;
411 #ifdef USE_HDF5
412     int tmp;
413 #endif
414 
415     CHECK(nc_inq(ncid, &ndims_in, &nvars_in, &natts_in, &unlimdimid_in));
416     if (ndims_in != (NDIMS0+extra) || nvars_in != (NVARS0+modified+extra) || natts_in != 1 || unlimdimid_in != 0)
417 	CHECK(NC_EINVAL);
418 
419     /* Get all the dimids */
420 #ifdef USE_HDF5
421     tmp = 0;
422     CHECK((nc_inq_dimids(ncid,&tmp,dimid,1)));
423     if(tmp != NDIMS0+extra) CHECK(NC_EINVAL);
424 
425     /* Get all the varids */
426     tmp = 0;
427     CHECK((nc_inq_varids(ncid,&tmp,varid)));
428     if(tmp != (NVARS0+modified+extra)) CHECK(NC_EINVAL);
429 #else
430     { /* Simulate nc_inq_varids and nc_inq_dimids */
431 	int j;
432 	int dimcnt = 0;
433         int varcnt = 0;
434 	CHECK(nc_inq(ncid, &dimcnt, &varcnt, NULL, NULL));
435 	for(j=0;j<dimcnt;j++) dimid[j] = j;
436 	for(j=0;j<varcnt;j++) varid[j] = j;
437     }
438 #endif
439 
440     CHECK(nc_get_att_text(ncid, NC_GLOBAL, ATT0_NAME, att0_in));
441     att0_in[sizeof(ATT0_TEXT)] = '\0';
442     if (strcmp(att0_in, ATT0_TEXT)) CHECK(NC_EINVAL);
443 
444     /* CHECK dimensions. */
445     CHECK(nc_inq_dim(ncid, dimid[0], name_in, &len_in));
446     if (strcmp(name_in, DIM0_NAME)) CHECK(NC_EINVAL);
447     CHECK(nc_inq_dim(ncid, dimid[1], name_in, &len_in));
448     if (strcmp(name_in, DIM1_NAME) || len_in != DIM1_LEN) CHECK(NC_EINVAL);
449     if(extra) {
450         CHECK(nc_inq_dim(ncid, dimid[2], name_in, &len_in));
451         if (strcmp(name_in, DIMX_NAME) || len_in != DIMX_LEN) CHECK(NC_EINVAL);
452     }
453 
454     /* CHECK variables. */
455     CHECK(nc_inq_var(ncid, varid[0], name_in, &type_in, &ndims_in, dimid_in, &natts_in));
456     if (strcmp(name_in, VAR0_NAME) || type_in != NC_INT || ndims_in != NDIMS0 ||
457     dimid_in[0] != 0 || dimid_in[1] != 1 || natts_in != 0) CHECK(NC_EINVAL);
458     CHECK(nc_inq_var(ncid, varid[1], name_in, &type_in, &ndims_in, dimid_in, &natts_in));
459     if (strcmp(name_in, VAR1_NAME) || type_in != NC_SHORT || ndims_in != 1 || dimid_in[0] != 1 || natts_in != 0)
460     	CHECK(NC_EINVAL);
461     CHECK(nc_inq_var(ncid, varid[2], name_in, &type_in, &ndims_in, dimid_in, &natts_in));
462     if (strcmp(name_in, VAR2_NAME) || type_in != NC_FLOAT || ndims_in != 0 || natts_in != 0)
463     	CHECK(NC_EINVAL);
464 
465     CHECK(nc_get_var_int(ncid, varid[0], nightdata_in));
466     for(i=0;i<dimprod;i++) {
467 	if(nightdata_in[i] != (100*i)) CHECK(NC_EINVAL);
468     }
469 
470     CHECK(nc_get_var_short(ncid, varid[1], taxi_distance_in));
471     for(i=0;i<DIM1_LEN;i++) {
472 	if(taxi_distance_in[i] != (i)) CHECK(NC_EINVAL);
473     }
474 
475     CHECK(nc_get_var_float(ncid, varid[2], &float_data_in));
476     if (float_data_in != FLOATVAL) CHECK(NC_EINVAL);
477 
478     if(modified) {
479 	size_t unlimlen;
480 	CHECK(nc_inq_var(ncid, varid[3], name_in, &type_in, &ndims_in, dimid_in, &natts_in));
481         if (strcmp(name_in, VAR3_NAME) || type_in != NC_INT || ndims_in != 1 ||
482 	    dimid_in[0] != 0 || natts_in != 0) CHECK(NC_EINVAL);
483         CHECK(nc_inq_dimlen(ncid, dimid_in[0], &unlimlen));
484         CHECK(nc_get_var_int(ncid, varid[3], milesdata_in));
485 	for(i=0;i<unlimlen;i++) {
486 	    if(milesdata_in[i] != i) CHECK(NC_EINVAL);
487 	}
488     }
489 
490     if(extra) {
491 	size_t xlen;
492 	CHECK(nc_inq_var(ncid, varid[4], name_in, &type_in, &ndims_in, dimid_in, &natts_in));
493         if (strcmp(name_in, VARX_NAME) || type_in != NC_INT || ndims_in != 1 ||
494 	    dimid_in[0] != dimid[2] || natts_in != 0) CHECK(NC_EINVAL);
495         CHECK(nc_inq_dimlen(ncid, dimid_in[0], &xlen));
496         CHECK(nc_get_var_int(ncid, varid[4], expenses_in));
497 	for(i=0;i<xlen;i++) {
498 	    if(expenses_in[i] != i) CHECK(NC_EINVAL);
499 	}
500     }
501 
502     return stat;
503 }
504 
505 void
memiofree(NC_memio * memio,void ** original)506 memiofree(NC_memio* memio, void** original)
507 {
508     if(memio != NULL) {
509 	if(memio->memory != NULL)
510 	    free(memio->memory);
511 	memio->memory = NULL;
512     }
513     if(original) *original = NULL;
514 }
515 
516 static int
test_open(const char * path,NC_memio * filedata,int mode)517 test_open(const char* path, NC_memio* filedata, int mode)
518 {
519     int stat = NC_NOERR;
520     NC_memio duplicate;
521     void* original = NULL;
522     NC_memio finaldata;
523     int ncid;
524     int xmode = mode; /* modified mode */
525 
526     finaldata.memory = NULL;
527     finaldata.size = 0;
528     finaldata.flags = 0;
529 
530     fprintf(stderr,"\n\t***Test open 1: nc_open_mem(): read-only\n");
531     CHECK(duplicatememory(filedata,&duplicate,0,&original));
532     CHECK(nc_open_mem(path, xmode, duplicate.size, duplicate.memory, &ncid));
533     CHECK(verify_file(ncid,!MODIFIED,!EXTRA));
534     CHECK(nc_close(ncid));
535     memiofree(&duplicate,&original);
536 
537     fprintf(stderr,"\n\t***Test open 2: nc_open_memio(): read-only\n");
538     CHECK(duplicatememory(filedata,&duplicate,0,&original));
539     duplicate.flags = NC_MEMIO_LOCKED;
540     CHECK(nc_open_memio(path, xmode, &duplicate, &ncid))
541     CHECK(verify_file(ncid,!MODIFIED,!EXTRA));
542     CHECK(nc_close_memio(ncid,&finaldata));
543     /* Published returned finaldata  */
544     fprintf(stderr,"\tfinaldata: size=%lld memory=%p original=%p\n",(unsigned long long)finaldata.size,finaldata.memory,original);
545     /* Verify that finaldata is same */
546     if(finaldata.size != duplicate.size) CHECK(NC_EINVAL);
547     if(finaldata.memory != duplicate.memory) CHECK(NC_EINVAL);
548     memiofree(&finaldata,&original);
549 
550     fprintf(stderr,"\n\t***Test open 3: nc_open_memio(): read-write, copy, no size increase\n");
551     fprintf(stderr,"\t*** Not testable\n");
552 #if 0
553     {
554 	fprintf(stderr,"\n\t***Test open 3: nc_open_memio(): read-write, copy, no size increase\n");
555 	xmode |= NC_WRITE; /* allow file to be modified */
556 	CHECK(duplicatememory(filedata,&duplicate,0,&original));
557 	CHECK(nc_open_memio(path, xmode, &duplicate, &ncid))
558 	/* modify file */
559 	CHECK(modify_file(ncid));
560 	CHECK(verify_file(ncid,MODIFIED,!EXTRA));
561 	CHECK(nc_close_memio(ncid,&finaldata));
562 	/* Published returned finaldata  */
563 	fprintf(stderr,"\tfinaldata: size=%lld memory=%p original=%p\n",(unsigned long long)finaldata.size,finaldata.memory,original);
564 	/* Verify that finaldata is same */
565 	if(finaldata.size < filedata->size) CHECK(NC_EINVAL);
566 	/* As a safeguard, the memory in duplicate should have been set to NULL*/
567 	memiofree(&finaldata,&original);
568     }
569 #endif
570 
571     fprintf(stderr,"\n\t***Test open 4: nc_open_memio(): read-write, copy, size increase\n");
572     xmode |= NC_WRITE; /* allow file to be modified */
573     CHECK(duplicatememory(filedata,&duplicate,0,&original));
574     CHECK(nc_open_memio(path, xmode, &duplicate, &ncid))
575     /* modify file */
576     CHECK(modify_file(ncid));
577     CHECK(modify_file_extra(ncid));
578     CHECK(verify_file(ncid,MODIFIED,EXTRA));
579     CHECK(nc_close_memio(ncid,&finaldata));
580     /* Published returned finaldata  */
581     fprintf(stderr,"\tfinaldata: size=%lld memory=%p original=%p\n",(unsigned long long)finaldata.size,finaldata.memory,original);
582     /* Verify that finaldata is same */
583     if(finaldata.size < filedata->size) CHECK(NC_EINVAL);
584     /* As a safeguard, the memory in duplicate should have been set to NULL*/
585     memiofree(&finaldata,&original);
586 
587     fprintf(stderr,"\n\t***Test open 5: nc_open_memio(): read-write, locked, extra space\n");
588     /* Store the filedata in a memory chunk that leaves room for modification */
589     CHECK(duplicatememory(filedata,&duplicate,LARGE_SPACE,&original));
590     /* Lock the duplicate memory */
591     duplicate.flags |= NC_MEMIO_LOCKED;
592     xmode |= NC_WRITE; /* allow file to be modified */
593     CHECK(nc_open_memio(path, xmode, &duplicate, &ncid))
594     /* modify file */
595     CHECK(modify_file(ncid));
596     CHECK(verify_file(ncid,MODIFIED,!EXTRA));
597     CHECK(nc_close_memio(ncid,&finaldata));
598     /* Published returned finaldata  */
599     fprintf(stderr,"\tfinaldata: size=%lld memory=%p original=%p\n",(unsigned long long)finaldata.size,finaldata.memory,original);
600     /* Check returned finaldata:
601        should have same memory but
602        actual used final size should not exceed the original */
603     if(finaldata.size > duplicate.size) CHECK(NC_EINVAL);
604     if(finaldata.memory != duplicate.memory) CHECK(NC_EINVAL);
605     memiofree(&finaldata,&original);
606     return stat;
607 }
608 
609 static int
test_create(const char * path,int mode)610 test_create(const char* path, int mode)
611 {
612     int stat = NC_NOERR;
613     NC_memio finaldata;
614     int ncid;
615     int xmode = mode;
616     finaldata.memory = NULL;
617     finaldata.size = 0;
618     finaldata.flags = 0;
619 
620     fprintf(stderr,"\n\t***Test create 1: nc_create_memio(): no initialsize\n");
621     CHECK(nc_create_mem(path, xmode, 0, &ncid))
622     /* create file metadata */
623     CHECK(define_metadata(ncid));
624     CHECK(verify_file(ncid,!MODIFIED,!EXTRA));
625     CHECK(nc_close_memio(ncid,&finaldata));
626     /* Published returned finaldata  */
627     fprintf(stderr,"\tfinaldata: size=%lld memory=%p\n",(unsigned long long)finaldata.size,finaldata.memory);
628     free(finaldata.memory);
629     fprintf(stderr,"\n\t***Test create 2: nc_create_memio(): initialsize; save file\n");
630     CHECK(nc_create_mem(path, xmode, LARGE_SPACE, &ncid))
631     /* create file metadata */
632     CHECK(define_metadata(ncid));
633     CHECK(verify_file(ncid,!MODIFIED,!EXTRA));
634     CHECK(nc_close_memio(ncid,&finaldata));
635     /* Published returned finaldata */
636     fprintf(stderr,"\tfinaldata: size=%lld memory=%p\n",(unsigned long long)finaldata.size,finaldata.memory);
637     /* Write out the final data as a .nc file */
638     CHECK(writefile(path,&finaldata));
639     if(finaldata.memory != NULL)
640         free(finaldata.memory);
641     finaldata.memory = NULL;
642     return stat;
643 }
644 
645 static int
test_misc(const char * path,int mode,NC_memio * filedata)646 test_misc(const char* path, int mode, NC_memio* filedata)
647 {
648     int stat = NC_NOERR;
649     int ncid;
650     int xmode = mode;
651     NC_memio duplicate;
652     void* original = NULL;
653 
654     fprintf(stderr,"\n\t***Test misc 1: use nc_close on created inmemory file\n");
655     CHECK(nc_create_mem(MISC, xmode, 0, &ncid))
656     CHECK(nc_close(ncid));
657     fprintf(stderr,"\t***Pass\n");
658     removefile(MISC);
659 
660     fprintf(stderr,"\n\t***Test misc 2: use nc_close on opened inmemory file\n");
661     CHECK(duplicatememory(filedata,&duplicate,0,&original));
662     CHECK(nc_open_memio(path, xmode, &duplicate, &ncid))
663     CHECK(verify_file(ncid,!MODIFIED,!EXTRA));
664     CHECK(nc_close(ncid));
665     fprintf(stderr,"\t***Pass\n");
666     /* Do not free: nc_close will have done it memiofree(&duplicate,&original); */
667     removefile(MISC);
668 
669     return stat;
670 }
671 
672 /* Test various edge conditions to ensure they fail correctly */
673 static int
test_xfail(const char * path,int mode,NC_memio * filedata)674 test_xfail(const char* path, int mode, NC_memio* filedata)
675 {
676     int stat = NC_NOERR;
677     NC_memio duplicate = {0,NULL,0};
678     int ncid;
679     int xmode = mode; /* modified mode */
680     void* original = NULL;
681 
682     fprintf(stderr,"\n\t***Test xfail 1: nc_open_mem(): write to read-only\n");
683     CHECK(duplicatememory(filedata,&duplicate,0,&original));
684     CHECK(nc_open_mem(XFAIL, xmode, duplicate.size, duplicate.memory, &ncid));
685     XCHECK(nc_redef(ncid));
686     CHECK(nc_abort(ncid));
687     memiofree(&duplicate,&original);
688 
689     fprintf(stderr,"\n\t***Test xfail 2: nc_open_memio(): modify without overallocating\n");
690     if((mode & NC_NETCDF4)) {
691         fprintf(stderr,"\t*** Suppressed because of HDF5 library bug\n");
692     } else {
693       /* With HDF5 1.8.20, and possibly other versions,
694          this tests causes a seg fault in the HDF5 Library.
695          So until it is fixed, just leave well enough alone */
696 	NC_memio finaldata;
697 	memset(&finaldata,0,sizeof(finaldata));
698 	CHECK(duplicatememory(filedata,&duplicate,0,&original));
699 	duplicate.flags = NC_MEMIO_LOCKED;
700 	xmode |= NC_WRITE;
701 	CHECK(nc_open_memio(XFAIL, xmode, &duplicate, &ncid))
702 	XCHECK(modify_file(ncid));
703 	CHECK(nc_abort(ncid));
704 	memiofree(&finaldata,&original);
705 	memiofree(&duplicate,&original);
706     }
707 
708     return stat;
709 }
710 
711 int
main(int argc,char ** argv)712 main(int argc, char **argv)
713 {
714     int stat = NC_NOERR;
715     NC_memio filedata3;
716     void* original = NULL;
717 #ifdef USE_HDF5
718     NC_memio filedata4;
719 #endif
720 
721     fprintf(stderr,"\n*** Testing the inmemory API: netcdf-3.\n");
722     CHECK(create_reference_file(FILE3,NC_NETCDF3,&filedata3)); /* netcdf-3 */
723     CHECK(test_open(FILE3,&filedata3,NC_NETCDF3));
724     CHECK(test_create(CREATE3,NC_NETCDF3));
725     CHECK(test_misc(FILE3, NC_NETCDF3, &filedata3));
726     CHECK(test_xfail(FILE3, NC_NETCDF3, &filedata3));
727     memiofree(&filedata3,&original);
728 
729 #ifdef USE_HDF5
730     fprintf(stderr,"\n*** Testing the inmemory API: netcdf-4.\n");
731     CHECK(create_reference_file(FILE4,NC_NETCDF4,&filedata4));
732     CHECK(test_open(FILE4,&filedata4,NC_NETCDF4));
733     CHECK(test_create(CREATE4,NC_NETCDF4));
734     CHECK(test_misc(FILE4,NC_NETCDF4, &filedata4));
735     CHECK(test_xfail(FILE4, NC_NETCDF4, &filedata4));
736     memiofree(&filedata4,&original);
737 #endif
738 
739     SUMMARIZE_ERR;
740 
741     FINAL_RESULTS;
742 
743     return 0;
744 }
745