From 6a06eaba15463330e0c96d0d99ad3dc4bc4565b2 Mon Sep 17 00:00:00 2001 From: Dennis Heimbigner <dennis.heimbigner@gmail.com> Date: Sun, 16 Mar 2025 18:13:51 -0600 Subject: [PATCH] Fix bug in copying compound types with variable length fields. re: Issue https://github.com/Unidata/netcdf-c/issues/3110 There was a two-line typo in the code in netcdf-c/libdispatch/distance_intern.c. This PR fixes it and adds a test case in netcdf-c/unit_test/tst_reclaim.c. --- libdispatch/dinstance.c | 1 + libdispatch/dinstance_intern.c | 4 +- unit_test/Makefile.am | 4 +- unit_test/reclaim_tests.baseline | 94 +++++++++++++++++++ unit_test/reclaim_tests.cdl | 14 +++ unit_test/run_reclaim_tests.sh | 4 + unit_test/tst_reclaim.c | 155 ++++++++++++++++++++++++++++--- 7 files changed, 258 insertions(+), 18 deletions(-) create mode 100644 unit_test/reclaim_tests.baseline diff --git a/libdispatch/dinstance.c b/libdispatch/dinstance.c index cef9b2e89..fbb2db714 100644 --- a/libdispatch/dinstance.c +++ b/libdispatch/dinstance.c @@ -284,6 +284,7 @@ done: @param xtype type id @param memory ptr to top-level memory to dump @param count number of instances of the type in memory block +@param bufp return ptr to a buffer of dump'd data @return error code */ diff --git a/libdispatch/dinstance_intern.c b/libdispatch/dinstance_intern.c index f1dac8eda..894ce40e2 100644 --- a/libdispatch/dinstance_intern.c +++ b/libdispatch/dinstance_intern.c @@ -439,8 +439,8 @@ copy_datar(NC_FILE_INFO_T* file, NC_TYPE_INFO_T* utype, Position src, Position d /* optimize: string field type */ if(field->nc_typeid == NC_STRING) { - char** srcstrvec = (char**)src.memory; - char** dststrvec = (char**)dst.memory; + char** srcstrvec = (char**)fsrc.memory; + char** dststrvec = (char**)fdst.memory; for(i=0;i<arraycount;i++) {if(srcstrvec[i] != NULL) {dststrvec[i] = strdup(srcstrvec[i]);} else {dststrvec[i] = NULL;}} continue; /* move to next field */ diff --git a/unit_test/Makefile.am b/unit_test/Makefile.am index e1ae642f1..a2f86e84d 100644 --- a/unit_test/Makefile.am +++ b/unit_test/Makefile.am @@ -70,8 +70,8 @@ EXTRA_DIST += nctest_netcdf4_classic.nc reclaim_tests.cdl EXTRA_DIST += ref_get.txt ref_set.txt EXTRA_DIST += ref_xget.txt ref_xset.txt EXTRA_DIST += ref_provparse.txt - -CLEANFILES = reclaim_tests*.txt reclaim_tests.nc tmp_*.txt +EXTRA_DIST += reclaim_tests.baseline +CLEANFILES = reclaim_tests*.txt reclaim_tests.nc reclaim_tests.dmp tmp_*.txt # If valgrind is present, add valgrind targets. @VALGRIND_CHECK_RULES@ diff --git a/unit_test/reclaim_tests.baseline b/unit_test/reclaim_tests.baseline new file mode 100644 index 000000000..92c5d2891 --- /dev/null +++ b/unit_test/reclaim_tests.baseline @@ -0,0 +1,94 @@ +netcdf reclaim_tests { +types: + int(*) vint_t ; + string(*) vstr_t ; + compound cmpd_atomic_t { + int f1 ; + float f2(2, 2) ; + }; // cmpd_atomic_t + compound cmpd_str_t { + string f1(3) ; + }; // cmpd_str_t + compound cmpd_str_att_t { + string field0 ; + int field1 ; + string fields2 ; + }; // cmpd_str_att_t + vint_t(*) vvint_t ; + vstr_t(*) vvstr_t ; + compound cmpd_vlen_t { + vint_t f1(2) ; + }; // cmpd_vlen_t + compound cmpd_cmpd_t { + vstr_t f1(2) ; + cmpd_vlen_t f2(2) ; + }; // cmpd_cmpd_t + cmpd_atomic_t(*) vcmpd_atomic_t ; + cmpd_str_t(*) vcmpd_str_t ; + cmpd_vlen_t(*) vcmpd_vlen_t ; + cmpd_cmpd_t(*) vcmpd_cmpd_t ; +dimensions: + d1 = 1 ; + d2 = 2 ; + d3 = 3 ; + d4 = 4 ; +variables: + int intvar(d4) ; + string strvar(d4) ; + vint_t vintvar(d4) ; + vstr_t vstrvar(d4) ; + vvint_t vvintvar(d4) ; + vvstr_t vvstrvar(d4) ; + cmpd_atomic_t catomvar(d2) ; + cmpd_str_t cstrvar(d2) ; + cmpd_vlen_t cvlenvar(d2) ; + cmpd_cmpd_t ccmpdvar(d2) ; + vcmpd_atomic_t vcatomvar ; + vcmpd_str_t vcstrvar(d2) ; + vcmpd_vlen_t vcvlenvar(d3) ; + vcmpd_cmpd_t vccmpdvar ; + int int3110 ; + cmpd_str_att_t int3110:stratt = {"field0", 31, "field3"} ; +data: + + intvar = 17, 13, 11, 7 ; + + strvar = "a", "ab", "abc", "abcd" ; + + vintvar = {1}, {1, 2}, {1, 2, 3}, {1, 2, 3, 4} ; + + vstrvar = {"a", "ab", "abc", "abcd"}, {"abcd", "abc", "ab"}, {"a", "ab"}, + {"abcd"} ; + + vvintvar = {{1}, {1, 2}}, {{1, 2, 3}}, {{1, 2, 3, 4}}, + {{17, 13}, {11, 7}, {5, 3}, {2, 1}} ; + + vvstrvar = {{"1"}, {"1", "2"}}, {{"1", "2", "3"}}, {{"1", "2", "3", "4"}}, + {{"17", "13"}, {"11", "7"}, {"5", "3"}, {"2", "1"}} ; + + catomvar = {17, {1.1, 2.2, 3.3, 4.4}}, {27, {4.1, 3.2, 3.3, 1.4}} ; + + cstrvar = {{"a", "abc", "abcd"}}, {{"x", "yz", "ijkl"}} ; + + cvlenvar = {{{17, 13}, {11}}}, {{{3, 5}, {7, 11}}} ; + + ccmpdvar = + {{{"xxx"}, {"yyy"}}, {{{{17, 13}, {12, 9}}}, {{{1, 3}, {2, 11}}}}}, + {{{"uv"}, {"w"}}, {{{{117, 113}, {112, 119}}}, {{{111, 113}, {112, 1111}}}}} ; + + vcatomvar = {{17, {1.1, 2.2, 3.3, 4.4}}, {27, {4.1, 3.2, 3.3, 1.4}}} ; + + vcstrvar = + {{{"c11a", "c12a", "c13a"}}, {{"c11b", "c12b", "c13b"}}, {{"c11c", "c12c", "c13c"}}, {{"c11d", "c12d", "c13d"}}}, + {{{"c21a", "c22a", "c23a"}}, {{"c21b", "c22b", "c23b"}}, {{"c21c", "c22c", "c23c"}}, {{"c21d", "c22d", "c23d"}}} ; + + vcvlenvar = + {{{{117, 113}, {111}}}, {{{13, 15}, {17, 111}}}, {{{217, 213}, {211}}}, {{{23, 25}, {27, 211}}}}, + {{{{2117, 2113}, {2211}}}, {{{2223, 2215}, {2227, 22111}}}, {{{22217, 22213}, {22211}}}, {{{2223, 2225}, {2227, 22211}}}}, + {{{{33117, 33113}, {33111}}}, {{{3313, 3315}, {3317, 33111}}}, {{{33217, 33213}, {33211}}}, {{{3323, 3325}, {3327, 33211}}}} ; + + vccmpdvar = + {{{{"xxx"}, {"yyy"}}, {{{{17, 13}, {12, 9}}}, {{{1, 3}, {2, 11}}}}}, {{{"uv"}, {"w"}}, {{{{117, 113}, {112, 119}}}, {{{111, 113}, {112, 1111}}}}}} ; + + int3110 = 117 ; +} diff --git a/unit_test/reclaim_tests.cdl b/unit_test/reclaim_tests.cdl index acf716793..352cec52f 100644 --- a/unit_test/reclaim_tests.cdl +++ b/unit_test/reclaim_tests.cdl @@ -19,6 +19,13 @@ types: vstr_t f1(2) ; cmpd_vlen_t f2(2); }; + /* Specific test types */ + /* ref: Issue https://github.com/Unidata/netcdf-c/issues/3110 */ + compound cmpd_str_att_t { /* compound with separate string typed field(s) */ + string field0 ; + int field1 ; + string fields2 ; + }; cmpd_atomic_t(*) vcmpd_atomic_t; cmpd_str_t(*) vcmpd_str_t; @@ -54,6 +61,10 @@ variables: vcmpd_vlen_t vcvlenvar(d3); vcmpd_cmpd_t vccmpdvar; + /* Special cases */ + int int3110; + cmpd_str_att_t int3110:stratt = {"field0", 31, "field3"} ; + data: intvar = 17, 13, 11, 7 ; strvar = "a", "ab", "abc", "abcd" ; @@ -70,6 +81,9 @@ data: vcvlenvar = {{{{117,113},{111}}}, {{{13,15},{17,111}}},{{{217,213},{211}}}, {{{23,25},{27,211}}}}, {{{{2117,2113},{2211}}}, {{{2223,2215},{2227,22111}}},{{{22217,22213},{22211}}}, {{{2223,2225},{2227,22211}}}}, {{{{33117,33113},{33111}}}, {{{3313,3315},{3317,33111}}},{{{33217,33213},{33211}}}, {{{3323,3325},{3327,33211}}}}; vccmpdvar = {{{{"xxx"},{"yyy"}},{{{{17,13},{12,9}}},{{{1,3},{2,11}}}}}, {{{"uv"},{"w"}},{{{{117,113},{112,119}}},{{{111,113},{112,1111}}}}}}; + /* Special cases */ + int3110 = 117; + } //reclaim_tests diff --git a/unit_test/run_reclaim_tests.sh b/unit_test/run_reclaim_tests.sh index d17777cf7..b6e2cdd26 100755 --- a/unit_test/run_reclaim_tests.sh +++ b/unit_test/run_reclaim_tests.sh @@ -11,6 +11,10 @@ ${execdir}/tst_reclaim > reclaim_tests.txt sed -e '/^(o)/p' -ed reclaim_tests.txt | sed -e 's/^(o) //' > reclaim_tests_o.txt sed -e '/^(c)/p' -ed reclaim_tests.txt | sed -e 's/^(c) //' > reclaim_tests_c.txt diff reclaim_tests_o.txt reclaim_tests_c.txt +# Also test using ncdump +${NCDUMP} reclaim_tests.nc > reclaim_tests.dmp +diff reclaim_tests.dmp ${srcdir}/reclaim_tests.baseline + diff --git a/unit_test/tst_reclaim.c b/unit_test/tst_reclaim.c index 869f3c71f..dbe0d8040 100644 --- a/unit_test/tst_reclaim.c +++ b/unit_test/tst_reclaim.c @@ -14,7 +14,6 @@ nc_dump_data => NC_dump_data See the file "reclaim_tests.cdl" to see the input file semantics. */ - #include "config.h" #include <stdlib.h> #include <stdio.h> @@ -22,6 +21,7 @@ See the file "reclaim_tests.cdl" to see the input file semantics. #include <assert.h> #include "netcdf.h" #include "netcdf_aux.h" +#include "ncbytes.h" #define NCCATCH #include "nclog.h" @@ -40,6 +40,8 @@ See the file "reclaim_tests.cdl" to see the input file semantics. #define MAXOBJECTS 1024 +#define MAXATTTXT 2048 + struct Options { int debug; } dumpoptions; @@ -56,12 +58,21 @@ struct Dim { size_t size; }; +struct Att { + char name[NC_MAX_NAME]; + int atype; + size_t len; + void* data; +}; + struct Var { char name[NC_MAX_NAME]; int vid; int tid; size_t dimprod; void* data; + int natts; + struct Att* atts; }; struct Metadata { @@ -79,6 +90,7 @@ struct Metadata { struct Type vcmpd_str_t; struct Type vcmpd_vlen_t; struct Type vcmpd_cmpd_t; + struct Type cmpd_str_att_t; /* dim ids */ struct Dim d1; struct Dim d2; @@ -98,6 +110,8 @@ struct Metadata { struct Var vcstrvar; struct Var vcvlenvar; struct Var vccmpdvar; + /* Specials */ + struct Var int3110; } metadata; /* atomic Types */ @@ -172,6 +186,29 @@ setupdim(int ncid, const char* name, struct Dim* dim) return NCTHROW(stat); } +static int +setupatts(int ncid, struct Var* var) +{ + int stat = NC_NOERR; + int i; + if((stat=nc_inq_varnatts(ncid,var->vid,&var->natts))) return NCTHROW(stat); + var->atts = (struct Att*)calloc(sizeof(struct Att),MAXOBJECTS); + if(var->atts == NULL) return NCTHROW(NC_ENOMEM); + + for(i=0;i<var->natts;i++) { + struct Att* att = &var->atts[i]; + struct Type* atype = NULL; + if((stat=nc_inq_attname(ncid,var->vid,i,att->name))) return NCTHROW(stat); + if((stat=nc_inq_att(ncid,var->vid,att->name,&att->atype,&att->len))) return NCTHROW(stat); + if((atype = typemap[att->atype])==NULL) return NCTHROW(NC_ENOTATT); + /* compute the space for the attribute */ + if((att->data = calloc(atype->size,att->len))==NULL) return NCTHROW(NC_ENOMEM); + /* Get the data */ + if((stat=nc_get_att(ncid,var->vid,att->name,att->data))) return NCTHROW(stat); + } + return NCTHROW(stat); +} + static int setupvar(int ncid, const char* name, struct Var* var) { @@ -190,6 +227,7 @@ setupvar(int ncid, const char* name, struct Var* var) product *= dimsizes[i]; } var->dimprod = product; + if((stat=setupatts(ncid,var))) return NCTHROW(stat); varmap[var->vid] = var; return NCTHROW(stat); } @@ -233,6 +271,10 @@ setup(struct Metadata* md) CHECK(setupvar(md->ncid,"vcvlenvar",&md->vcvlenvar)); CHECK(setupvar(md->ncid,"vccmpdvar",&md->vccmpdvar)); + /* Specials */ + CHECK(setuptype(md->ncid,"cmpd_str_att_t",&md->cmpd_str_att_t)); + CHECK(setupvar(md->ncid,"int3110",&md->int3110)); /* will also setup associated attrs */ + done: assert(stat == NC_NOERR); } @@ -251,10 +293,50 @@ readvar(int ncid, struct Var* v) } static int -dumpvar(int ncid, struct Var* v, void* data, char** bufp) +dumpvar(int ncid, struct Var* v, void* data, NCbytes* buf) { int stat = NC_NOERR; - if((stat=ncaux_dump_data(ncid,v->tid,data,v->dimprod,bufp))) return NCTHROW(stat); + char* tmpbuf = NULL; + if((stat=ncaux_dump_data(ncid,v->tid,data,v->dimprod,&tmpbuf))) return NCTHROW(stat); + ncbytescat(buf,tmpbuf); + if(tmpbuf) free(tmpbuf); + return NCTHROW(stat); +} + +static int +dumpatt(int ncid, struct Var* v, struct Att* a, NCbytes* attbuf) +{ + int stat = NC_NOERR; + char* tmpbuf = NULL; + (void)v; /* unused */ + if((stat=ncaux_dump_data(ncid,a->atype,a->data,a->len,&tmpbuf))) return NCTHROW(stat); + ncbytescat(attbuf,tmpbuf); + if(tmpbuf) free(tmpbuf); + return NCTHROW(stat); +} + +static int +testatts(int ncid, struct Var* v, NCbytes* attbuf) +{ + int stat = NC_NOERR; + NCbytes* databuf = ncbytesnew(); + /* Print any attributes */ + if(v->natts > 0) { + int i; + for(i=0;i<v->natts;i++) { + struct Att* att = &v->atts[i]; + struct Type* atype = typemap[att->atype]; + ncbytescat(attbuf,"(o)\t"); + ncbytescat(attbuf,atype->name); + ncbytescat(attbuf,att->name); + ncbytescat(attbuf," = {"); + ncbytesclear(databuf); + if((stat=dumpatt(ncid, v, att, databuf))) return NCTHROW(stat); + ncbytescat(attbuf,ncbytescontents(databuf)); + ncbytescat(attbuf,"}\n"); + } + } + ncbytesfree(databuf); return NCTHROW(stat); } @@ -262,30 +344,73 @@ static int testvar(int ncid, struct Var* v) { int stat = NC_NOERR; + NCbytes* tmpbuf = ncbytesnew(); + NCbytes* origbuf = ncbytesnew(); + NCbytes* copybuf = ncbytesnew(); char* buforig = NULL; char* bufcopy = NULL; + char* attbuforig = NULL; + char* attbufcopy = NULL; void* copy = NULL; - + /* Test original */ if((stat=readvar(ncid,v))) return NCTHROW(stat); - if((stat=dumpvar(ncid,v,v->data,&buforig))) return NCTHROW(stat); - printf("(o) %s: %s\n",v->name,buforig); - // Test copying + if((stat=dumpvar(ncid,v,v->data,tmpbuf))) return NCTHROW(stat); + ncbytescat(origbuf,"(o) "); + ncbytescat(origbuf,"v->name"); + ncbytescat(origbuf,": "); + ncbytescat(origbuf,ncbytescontents(tmpbuf)); + ncbytescat(origbuf,"\n"); + /* capture */ + buforig = ncbytesextract(origbuf); + /* Print any attributes */ + if(v->natts > 0) { + ncbytesclear(tmpbuf); + testatts(ncid,v,tmpbuf); + attbuforig = ncbytesextract(tmpbuf); + } + + /* Test copying */ if((stat=nc_copy_data_all(ncid,v->tid,v->data,v->dimprod,©))) return NCTHROW(stat); - // Print copy - if((stat=dumpvar(ncid,v,copy,&bufcopy))) return NCTHROW(stat); - printf("(c) %s: %s\n",v->name,bufcopy); + ncbytesclear(tmpbuf); + if((stat=dumpvar(ncid,v,copy,tmpbuf))) return NCTHROW(stat); + ncbytescat(copybuf,"(o) "); + ncbytescat(copybuf,"v->name"); + ncbytescat(copybuf,": "); + ncbytescat(copybuf,ncbytescontents(tmpbuf)); + ncbytescat(copybuf,"\n"); + /* capture */ + bufcopy = ncbytesextract(copybuf); + /* Print any attributes */ + if(v->natts > 0) { + ncbytesclear(tmpbuf); + testatts(ncid,v,tmpbuf); + attbufcopy = ncbytesextract(tmpbuf); + } + /* Compare */ if(strcmp(buforig,bufcopy) != 0) fprintf(stderr,"*** orig != copy\n"); - if(buforig) free(buforig); - if(bufcopy) free(bufcopy); - // reclaim original + if(v->natts) { + if(strcmp(attbuforig,attbufcopy) != 0) + fprintf(stderr,"*** attribute orig != attribute copy\n"); + } + + /* reclaim original */ if((stat=nc_reclaim_data_all(ncid,v->tid,v->data,v->dimprod))) return NCTHROW(stat); - // reclaim copy + /* reclaim copy */ if((stat=nc_reclaim_data_all(ncid,v->tid,copy,v->dimprod))) return NCTHROW(stat); v->data = NULL; + + ncbytesfree(tmpbuf); + ncbytesfree(origbuf); + ncbytesfree(copybuf); + if(buforig) free(buforig); + if(bufcopy) free(bufcopy); + if(attbuforig) free(attbuforig); + if(attbufcopy) free(attbufcopy); + return NCTHROW(stat); } @@ -307,6 +432,8 @@ test(struct Metadata* md) CHECK(testvar(md->ncid,&md->vcstrvar)); CHECK(testvar(md->ncid,&md->vcvlenvar)); CHECK(testvar(md->ncid,&md->vccmpdvar)); + /* Specials */ + CHECK(testvar(md->ncid,&md->int3110)); done: return; }