Mercurial > hg > octave-terminal
changeset 10742:604e13a89c7f
initial code for structs rewrite
author | Jaroslav Hajek <highegg@gmail.com> |
---|---|
date | Tue, 22 Jun 2010 15:22:36 +0200 |
parents | b8d76f4be94a |
children | cb3ed842bd30 |
files | src/DLD-FUNCTIONS/onCleanup.cc src/oct-map.cc src/oct-map.h src/ov-base.cc src/ov-base.h src/ov-cell.h src/ov-class.cc src/ov-class.h src/ov-fcn-inline.cc src/ov-fcn-inline.h src/ov-struct.cc src/ov-struct.h src/ov.cc src/ov.h |
diffstat | 14 files changed, 2723 insertions(+), 484 deletions(-) [+] |
line wrap: on
line diff
--- a/src/DLD-FUNCTIONS/onCleanup.cc +++ b/src/DLD-FUNCTIONS/onCleanup.cc @@ -58,7 +58,10 @@ bool is_object (void) const { return true; } // do we want this? - Octave_map map_value (void) const; + octave_map map_value (void) const + { return scalar_map_value (); } + + octave_scalar_map scalar_map_value (void) const; dim_vector dims (void) const { static dim_vector dv (1, 1); return dv; } @@ -169,11 +172,11 @@ } } -Octave_map -octave_oncleanup::map_value (void) const +octave_scalar_map +octave_oncleanup::scalar_map_value (void) const { - Octave_map retval; - retval.assign ("task", fcn); + octave_scalar_map retval; + retval.setfield ("task", fcn); return retval; }
--- a/src/oct-map.cc +++ b/src/oct-map.cc @@ -2,6 +2,7 @@ Copyright (C) 1995, 1996, 1997, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 John W. Eaton +Copyright (C) 2010 VZLU Prague This file is part of Octave. @@ -31,6 +32,1072 @@ #include "oct-map.h" #include "utils.h" +octave_fields::fields_rep octave_fields::nil_rep; + +octave_fields::octave_fields (const string_vector& fields) + : rep (new fields_rep) +{ + octave_idx_type n = fields.numel (); + for (octave_idx_type i = 0; i < n; i++) + (*rep)[fields(i)] = i; +} + +bool +octave_fields::isfield (const std::string& field) const +{ + return rep->find (field) != rep->end (); +} + +octave_idx_type +octave_fields::getfield (const std::string& field) const +{ + fields_rep::iterator p = rep->find (field); + return (p != rep->end ()) ? p->second : -1; +} + +octave_idx_type +octave_fields::getfield (const std::string& field) +{ + fields_rep::iterator p = rep->find (field); + if (p != rep->end ()) + return p->second; + else + { + make_unique (); + octave_idx_type n = rep->size (); + return (*rep)[field] = n; + } +} + +octave_idx_type +octave_fields::rmfield (const std::string& field) +{ + fields_rep::iterator p = rep->find (field); + if (p == rep->end ()) + return -1; + else + { + octave_idx_type n = p->second; + make_unique (); + rep->erase (field); + for (fields_rep::iterator q = rep->begin (); q != rep->end (); q++) + { + if (q->second >= n) + q->second--; + } + + return n; + } +} + +void +octave_fields::orderfields (Array<octave_idx_type>& perm) +{ + octave_idx_type n = rep->size (); + perm.clear (n, 1); + + make_unique (); + octave_idx_type i = 0; + for (fields_rep::iterator q = rep->begin (); q != rep->end (); q++) + { + octave_idx_type j = q->second; + q->second = i; + perm(i++) = j; + } +} + +bool +octave_fields::equal_up_to_order (const octave_fields& other, + Array<octave_idx_type>& perm) const +{ + bool retval = true; + + octave_idx_type n = nfields (); + if (perm.length () != n) + perm.clear (1, n); + else + perm.make_unique (); // optimization + + iterator p = begin (), q = other.begin (); + for (; p != end () && q != other.end (); p++, q++) + { + if (p->first == q->first) + perm.xelem(p->second) = q->second; + else + { + retval = false; + break; + } + } + + retval = (p == end () && q == other.end ()); + + return retval; +} + +string_vector +octave_fields::fieldnames (void) const +{ + octave_idx_type n = nfields (); + string_vector retval(n); + + for (iterator p = begin (); p != end (); p++) + retval.xelem(p->second) = p->first; + + return retval; +} + +octave_value +octave_scalar_map::getfield (const std::string& k) const +{ + octave_idx_type idx = keys.getfield (k); + return (idx >= 0) ? vals[idx] : octave_value (); +} + +void +octave_scalar_map::setfield (const std::string& k, const octave_value& val) +{ + octave_idx_type idx = keys.getfield (k); + if (idx < static_cast<octave_idx_type> (vals.size ())) + vals[idx] = val; + else + vals.push_back (val); +} + +void +octave_scalar_map::rmfield (const std::string& k) +{ + octave_idx_type idx = keys.rmfield (k); + if (idx >= 0) + vals.erase (vals.begin () + idx); +} + +octave_scalar_map +octave_scalar_map::orderfields (void) const +{ + Array<octave_idx_type> perm; + return orderfields (perm); +} + +octave_scalar_map +octave_scalar_map::orderfields (Array<octave_idx_type>& perm) const +{ + octave_scalar_map retval (keys); + retval.keys.orderfields (perm); + + octave_idx_type nf = nfields (); + for (octave_idx_type i = 0; i < nf; i++) + retval.vals[i] = vals[perm.xelem(i)]; + + return retval; +} + +octave_scalar_map +octave_scalar_map::orderfields (const octave_scalar_map& other, + Array<octave_idx_type>& perm) const +{ + if (keys.is_same (other.keys)) + return *this; + else + { + octave_scalar_map retval (other.keys); + if (other.keys.equal_up_to_order (keys, perm)) + { + octave_idx_type nf = nfields (); + for (octave_idx_type i = 0; i < nf; i++) + retval.vals[i] = vals[perm.xelem(i)]; + } + else + error ("orderfields: structs must have same fields up to order"); + + return retval; + } +} + +octave_value +octave_scalar_map::contents (const std::string& k) const +{ + return getfield (k); +} + +octave_value& +octave_scalar_map::contents (const std::string& k) +{ + octave_idx_type idx = keys.getfield (k); + if (idx >= static_cast<octave_idx_type> (vals.size ())) + vals.resize (idx); + return vals[idx]; +} + +octave_map::octave_map (const octave_scalar_map& m) + : keys (m.keys), vals (), dimensions (1, 1) +{ + octave_idx_type nf = m.nfields (); + vals.reserve (nf); + for (octave_idx_type i = 0; i < nf; i++) + { + vals.push_back (Cell (dimensions)); + vals[i].xelem(0) = m.vals[i]; + } +} + +octave_map::octave_map (const Octave_map& m) + : keys (m.keys ()), vals (m.nfields ()), dimensions (m.dims ()) +{ + for (iterator p = begin (); p != end (); p++) + contents(p) = m.contents (key (p)); + + optimize_dimensions (); +} + +Cell +octave_map::getfield (const std::string& k) const +{ + octave_idx_type idx = keys.getfield (k); + return (idx >= 0) ? vals[idx] : Cell (); +} + +void +octave_map::setfield (const std::string& k, const Cell& val) +{ + if (nfields () == 0) + dimensions = val.dims (); + + if (val.dims () == dimensions) + { + octave_idx_type idx = keys.getfield (k); + if (idx < static_cast<octave_idx_type> (vals.size ())) + vals[idx] = val; + else + vals.push_back (val); + } + else + error ("octave_map::setfield: internal error"); +} + +void +octave_map::rmfield (const std::string& k) +{ + octave_idx_type idx = keys.rmfield (k); + if (idx >= 0) + vals.erase (vals.begin () + idx); +} + +octave_map +octave_map::orderfields (void) const +{ + Array<octave_idx_type> perm; + return orderfields (perm); +} + +octave_map +octave_map::orderfields (Array<octave_idx_type>& perm) const +{ + octave_map retval (keys); + retval.keys.orderfields (perm); + + octave_idx_type nf = nfields (); + for (octave_idx_type i = 0; i < nf; i++) + retval.vals[i] = vals[perm.xelem(i)]; + + return retval; +} + +octave_map +octave_map::orderfields (const octave_map& other, + Array<octave_idx_type>& perm) const +{ + if (keys.is_same (other.keys)) + return *this; + else + { + octave_map retval (other.keys); + if (other.keys.equal_up_to_order (keys, perm)) + { + octave_idx_type nf = nfields (); + for (octave_idx_type i = 0; i < nf; i++) + retval.vals[i] = vals[perm.xelem(i)]; + } + else + error ("orderfields: structs must have same fields up to order"); + + return retval; + } +} + +Cell +octave_map::contents (const std::string& k) const +{ + return getfield (k); +} + +Cell& +octave_map::contents (const std::string& k) +{ + octave_idx_type idx = keys.getfield (k); + if (idx >= static_cast<octave_idx_type> (vals.size ())) + vals.push_back (Cell (dimensions)); // auto-set correct dims. + return vals[idx]; +} + +void +octave_map::extract_scalar (octave_scalar_map& dest, + octave_idx_type idx) const +{ + octave_idx_type nf = nfields (); + dest.vals.reserve (nf); + for (octave_idx_type i = 0; i < nf; i++) + dest.vals.push_back (vals[i](idx)); +} + +octave_scalar_map +octave_map::checkelem (octave_idx_type n) const +{ + octave_scalar_map retval (keys); + + // Optimize this so that there is just one check. + extract_scalar (retval, compute_index (n, dimensions)); + + return retval; +} + +octave_scalar_map +octave_map::checkelem (octave_idx_type i, octave_idx_type j) const +{ + octave_scalar_map retval (keys); + + // Optimize this so that there is just one check. + extract_scalar (retval, compute_index (i, j, dimensions)); + + return retval; +} + +octave_scalar_map +octave_map::checkelem (const Array<octave_idx_type>& ra_idx) const +{ + octave_scalar_map retval (keys); + + // Optimize this so that there is just one check. + extract_scalar (retval, compute_index (ra_idx, dimensions)); + + return retval; +} + +octave_map +octave_map::squeeze (void) const +{ + octave_map retval (*this); + octave_idx_type nf = nfields (); + + retval.dimensions = dimensions.squeeze (); + + for (octave_idx_type i = 0; i < nf; i++) + retval.vals[i] = vals[i].squeeze (); + + retval.optimize_dimensions (); + + return retval; +} + +/* +%!# test preservation of keys by squeeze +%!test +%! x(1,1,1,1).d = 10; x(3,5,1,7).a = "b"; x(2,4,1,7).f = 27; +%! assert (fieldnames (squeeze (x)), {"d"; "a"; "f"}); +*/ + +octave_map +octave_map::permute (const Array<int>& vec, bool inv) const +{ + octave_map retval (keys); + octave_idx_type nf = nfields (); + + for (octave_idx_type i = 0; i < nf; i++) + retval.vals[i] = vals[i].permute (vec, inv); + + // FIXME: + // There is no dim_vector::permute for technical reasons. + // We pick the dim vector from results if possible, otherwise use a dummy + // array to get it. Need (?) a better solution to this problem. + if (nf > 0) + retval.dimensions = retval.vals[0].dims (); + else + { + Array<char> dummy (dimensions); + dummy = dummy.permute (vec, inv); + retval.dimensions = dummy.dims (); + } + + retval.optimize_dimensions (); + + return retval; +} + +/* +%!# test preservation of key order by permute +%!test +%! x(1,1,1,1).d = 10; x(3,5,1,7).a = "b"; x(2,4,1,7).f = 27; +%! assert (fieldnames (permute (x, [3, 4, 1, 2])), {"d"; "a"; "f"}); +*/ + +octave_map +octave_map::transpose (void) const +{ + assert (ndims () == 2); + + octave_map retval (keys); + + retval.dimensions = dim_vector (dimensions (1), dimensions (0)); + + octave_idx_type nf = nfields (); + for (octave_idx_type i = 0; i < nf; i++) + retval.vals[i] = vals[i].transpose (); + + retval.optimize_dimensions (); + + return retval; +} + +/* +%!# test preservation of key order by transpose +%!test +%! x(1,1).d = 10; x(3,5).a = "b"; x(2,4).f = 27; +%! assert (fieldnames (transpose (x)), {"d"; "a"; "f"}); +%! assert (fieldnames (x'), {"d"; "a"; "f"}); +%! assert (fieldnames (x.'), {"d"; "a"; "f"}); +*/ + +octave_map +octave_map::reshape (const dim_vector& dv) const +{ + octave_map retval (keys); + retval.dimensions = dv; + + octave_idx_type nf = nfields (); + if (nf > 0) + { + retval.vals.reserve (nf); + for (octave_idx_type i = 0; i < nf; i++) + retval.vals[i] = vals[i].reshape (dv); + } + else + { + // FIXME: Do it with a dummy array, to reuse error message. + // Need (?) a better solution. + Array<char> dummy (dimensions); + dummy.reshape (dv); + } + + retval.optimize_dimensions (); + + return retval; +} + +/* +%!# test preservation of key order by reshape +%!test +%! x(1,1).d = 10; x(4,6).a = "b"; x(2,4).f = 27; +%! assert (fieldnames (reshape (x, 3, 8)), {"d"; "a"; "f"}); +*/ + +void +octave_map::resize (const dim_vector& dv) +{ + octave_idx_type nf = nfields (); + if (nf > 0) + { + for (octave_idx_type i = 0; i < nf; i++) + vals[i].resize (dv); + } + else + { + // FIXME: Do it with a dummy array, to reuse error message. + // Need (?) a better solution. + Array<char> dummy (dimensions); + dummy.resize (dv); + } + + dimensions = dv; + optimize_dimensions (); +} + +void +octave_map::do_cat (int dim, octave_idx_type n, const octave_scalar_map *map_list, + octave_map& retval) +{ + octave_idx_type nf = retval.nfields (); + retval.vals.reserve (nf); + + dim_vector& rd = retval.dimensions; + rd.resize (dim+1, 1); + rd(0) = rd(1) = 1; + rd(dim) = n; + + for (octave_idx_type j = 0; j < nf; j++) + { + retval.vals.push_back (Cell (rd)); + assert (retval.vals[j].numel () == n); + for (octave_idx_type i = 0; i < n; i++) + retval.vals[j].xelem(i) = map_list[i].vals[j]; + } +} + +void +octave_map::do_cat (int dim, octave_idx_type n, const octave_map *map_list, + octave_map& retval) +{ + octave_idx_type nf = retval.nfields (); + retval.vals.reserve (nf); + + OCTAVE_LOCAL_BUFFER (Array<octave_value>, field_list, n); + + for (octave_idx_type j = 0; j < nf; j++) + { + for (octave_idx_type i = 0; i < n; i++) + field_list[i] = map_list[i].vals[j]; + + retval.vals.push_back (Array<octave_value>::cat (dim, n, field_list)); + } +} + +template <class map> +static void +permute_to_correct_order (octave_idx_type n, octave_idx_type nf, + const map *map_list, map *new_map_list) +{ + new_map_list[0] = map_list[0]; + + Array<octave_idx_type> perm (1, nf); + + for (octave_idx_type i = 1; i < n; i++) + { + new_map_list[i] = map_list[i].orderfields (map_list[0], perm); + + if (error_state) + { + // Use liboctave exception to be consistent. + (*current_liboctave_error_handler) + ("cat: field names mismatch in concatenating structs"); + break; + } + } +} + + +octave_map +octave_map::cat (int dim, octave_idx_type n, const octave_scalar_map *map_list) +{ + octave_map retval; + if (n > 0) + { + retval.keys = map_list[0].keys; + octave_idx_type nf = map_list[0].nfields (); + if (nf > 0) + { + // Try the fast case. + bool all_same = true; + for (octave_idx_type i = 1; i < n; i++) + { + all_same = map_list[0].keys.is_same (map_list[i].keys); + if (! all_same) + break; + } + + if (all_same) + do_cat (dim, n, map_list, retval); + else + { + // permute all structures to common order. + OCTAVE_LOCAL_BUFFER (octave_scalar_map, new_map_list, n); + + permute_to_correct_order (n, nf, map_list, new_map_list); + + do_cat (dim, n, new_map_list, retval); + } + + } + else + { + dim_vector& rd = retval.dimensions; + rd.resize (dim+1, 1); + rd(0) = rd(1) = 1; + rd(dim) = n; + } + + retval.optimize_dimensions (); + } + + return retval; +} + +octave_map +octave_map::cat (int dim, octave_idx_type n, const octave_map *map_list) +{ + octave_map retval; + if (n > 0) + { + retval.keys = map_list[0].keys; + octave_idx_type nf = map_list[0].nfields (); + + // Try the fast case. + bool all_same = true; + for (octave_idx_type i = 1; i < n; i++) + { + all_same = map_list[0].keys.is_same (map_list[i].keys); + if (! all_same) + break; + } + + if (all_same) + do_cat (dim, n, map_list, retval); + else + { + // permute all structures to correct order. + OCTAVE_LOCAL_BUFFER (octave_map, new_map_list, n); + + permute_to_correct_order (n, nf, map_list, new_map_list); + + if (nf > 0) + do_cat (dim, n, new_map_list, retval); + else + { + // Use dummy arrays. FIXME: Need(?) a better solution. + OCTAVE_LOCAL_BUFFER (Array<char>, dummy, n); + for (octave_idx_type i = 0; i < n; i++) + dummy[i].clear (map_list[0].dimensions); + Array<char>::cat (dim, n, dummy); + } + } + + retval.optimize_dimensions (); + } + + return retval; +} + +/* +%!# test preservation of key order by concatenation +%!test +%! x(1, 1).d = 10; x(4, 6).a = "b"; x(2, 4).f = 27; +%! y(1, 6).f = 11; y(1, 6).a = "c"; y(1, 6).d = 33; +%! assert (fieldnames ([x; y]), {"d"; "a"; "f"}); +*/ + +octave_map +octave_map::index (const idx_vector& i, bool resize_ok) const +{ + octave_map retval (keys); + octave_idx_type nf = nfields (); + + for (octave_idx_type k = 0; k < nf; k++) + retval.vals[k] = vals[k].index (i, resize_ok); + + if (nf > 0) + retval.dimensions = retval.vals[0].dims (); + else + { + // Use dummy array. FIXME: Need(?) a better solution. + Array<char> dummy (dimensions); + dummy = dummy.index (i, resize_ok); + retval.dimensions = dummy.dims (); + } + + retval.optimize_dimensions (); + + return retval; +} + +octave_map +octave_map::index (const idx_vector& i, const idx_vector& j, + bool resize_ok) const +{ + octave_map retval (keys); + octave_idx_type nf = nfields (); + + for (octave_idx_type k = 0; k < nf; k++) + retval.vals[k] = vals[k].index (i, j, resize_ok); + + if (nf > 0) + retval.dimensions = retval.vals[0].dims (); + else + { + // Use dummy array. FIXME: Need(?) a better solution. + Array<char> dummy (dimensions); + dummy = dummy.index (i, j, resize_ok); + retval.dimensions = dummy.dims (); + } + + retval.optimize_dimensions (); + + return retval; +} + +octave_map +octave_map::index (const Array<idx_vector>& ia, bool resize_ok) const +{ + octave_map retval (keys); + octave_idx_type nf = nfields (); + + for (octave_idx_type k = 0; k < nf; k++) + retval.vals[k] = vals[k].index (ia, resize_ok); + + if (nf > 0) + retval.dimensions = retval.vals[0].dims (); + else + { + // Use dummy array. FIXME: Need(?) a better solution. + Array<char> dummy (dimensions); + dummy = dummy.index (ia, resize_ok); + retval.dimensions = dummy.dims (); + } + + retval.optimize_dimensions (); + + return retval; +} + +octave_map +octave_map::index (const octave_value_list& idx, bool resize_ok) const +{ + octave_idx_type n_idx = idx.length (); + octave_map retval; + + switch (n_idx) + { + case 1: + { + idx_vector i = idx(0).index_vector (); + + if (! error_state) + retval = index (i, resize_ok); + } + break; + + case 2: + { + idx_vector i = idx(0).index_vector (); + + if (! error_state) + { + idx_vector j = idx(1).index_vector (); + + retval = index (i, j, resize_ok); + } + } + break; + + default: + { + Array<idx_vector> ia (n_idx, 1); + + for (octave_idx_type i = 0; i < n_idx; i++) + { + ia(i) = idx(i).index_vector (); + + if (error_state) + break; + } + + if (! error_state) + retval = index (ia, resize_ok); + } + break; + } + + return retval; +} + +void +octave_map::assign (const idx_vector& i, const octave_map& rhs) +{ + if (rhs.keys.is_same (keys)) + { + octave_idx_type nf = nfields (); + + for (octave_idx_type k = 0; k < nf; k++) + vals[k].assign (i, rhs.vals[k]); + + if (nf > 0) + dimensions = vals[0].dims (); + else + { + // Use dummy array. FIXME: Need(?) a better solution. + Array<char> dummy (dimensions), rhs_dummy (rhs.dimensions); + dummy.assign (i, rhs_dummy);; + dimensions = dummy.dims (); + } + + optimize_dimensions (); + } + else + { + Array<octave_idx_type> perm; + octave_map rhs1 = rhs.orderfields (*this, perm); + if (! error_state) + { + assert (rhs1.keys.is_same (rhs.keys)); + assign (i, rhs1); + } + else + error ("incompatible fields in struct assignment"); + } +} + +void +octave_map::assign (const idx_vector& i, const idx_vector& j, + const octave_map& rhs) +{ + if (rhs.keys.is_same (keys)) + { + octave_idx_type nf = nfields (); + + for (octave_idx_type k = 0; k < nf; k++) + vals[k].assign (i, j, rhs.vals[k]); + + if (nf > 0) + dimensions = vals[0].dims (); + else + { + // Use dummy array. FIXME: Need(?) a better solution. + Array<char> dummy (dimensions), rhs_dummy (rhs.dimensions); + dummy.assign (i, j, rhs_dummy);; + dimensions = dummy.dims (); + } + + optimize_dimensions (); + } + else + { + Array<octave_idx_type> perm; + octave_map rhs1 = rhs.orderfields (*this, perm); + if (! error_state) + { + assert (rhs1.keys.is_same (rhs.keys)); + assign (i, j, rhs1); + } + else + error ("incompatible fields in struct assignment"); + } +} + +void +octave_map::assign (const Array<idx_vector>& ia, + const octave_map& rhs) +{ + if (rhs.keys.is_same (keys)) + { + octave_idx_type nf = nfields (); + + for (octave_idx_type k = 0; k < nf; k++) + vals[k].assign (ia, rhs.vals[k]); + + if (nf > 0) + dimensions = vals[0].dims (); + else + { + // Use dummy array. FIXME: Need(?) a better solution. + Array<char> dummy (dimensions), rhs_dummy (rhs.dimensions); + dummy.assign (ia, rhs_dummy);; + dimensions = dummy.dims (); + } + + optimize_dimensions (); + } + else + { + Array<octave_idx_type> perm; + octave_map rhs1 = rhs.orderfields (*this, perm); + if (! error_state) + { + assert (rhs1.keys.is_same (rhs.keys)); + assign (ia, rhs1); + } + else + error ("incompatible fields in struct assignment"); + } +} + +void +octave_map::assign (const octave_value_list& idx, const octave_map& rhs) +{ + octave_idx_type n_idx = idx.length (); + + switch (n_idx) + { + case 1: + { + idx_vector i = idx(0).index_vector (); + + if (! error_state) + assign (i, rhs); + } + break; + + case 2: + { + idx_vector i = idx(0).index_vector (); + + if (! error_state) + { + idx_vector j = idx(1).index_vector (); + + assign (i, j, rhs); + } + } + break; + + default: + { + Array<idx_vector> ia (n_idx, 1); + + for (octave_idx_type i = 0; i < n_idx; i++) + { + ia(i) = idx(i).index_vector (); + + if (error_state) + break; + } + + if (! error_state) + assign (ia, rhs); + } + break; + } +} + +void +octave_map::delete_elements (const idx_vector& i) +{ + octave_idx_type nf = nfields (); + for (octave_idx_type k = 0; k < nf; k++) + vals[k].delete_elements (i); + + if (nf > 0) + dimensions = vals[0].dims (); + else + { + // Use dummy array. FIXME: Need(?) a better solution. + Array<char> dummy (dimensions); + dummy.delete_elements (i); + dimensions = dummy.dims (); + } + + optimize_dimensions (); +} + +void +octave_map::delete_elements (int dim, const idx_vector& i) +{ + octave_idx_type nf = nfields (); + for (octave_idx_type k = 0; k < nf; k++) + vals[k].delete_elements (dim, i); + + if (nf > 0) + dimensions = vals[0].dims (); + else + { + // Use dummy array. FIXME: Need(?) a better solution. + Array<char> dummy (dimensions); + dummy.delete_elements (dim, i); + dimensions = dummy.dims (); + } + + optimize_dimensions (); +} + +void +octave_map::delete_elements (const Array<idx_vector>& ia) +{ + octave_idx_type nf = nfields (); + for (octave_idx_type k = 0; k < nf; k++) + vals[k].delete_elements (ia); + + if (nf > 0) + dimensions = vals[0].dims (); + else + { + // Use dummy array. FIXME: Need(?) a better solution. + Array<char> dummy (dimensions); + dummy.delete_elements (ia); + dimensions = dummy.dims (); + } + + optimize_dimensions (); +} + +void +octave_map::delete_elements (const octave_value_list& idx) +{ + octave_idx_type n_idx = idx.length (); + + Array<idx_vector> ia (n_idx, 1); + + for (octave_idx_type i = 0; i < n_idx; i++) + { + ia(i) = idx(i).index_vector (); + + if (error_state) + break; + } + + if (! error_state) + delete_elements (ia); +} + +/* +%!# test preservation of key order by indexing +%!test +%! x(1, 1).d = 10; x(4, 6).a = "b"; x(2, 4).f = 27; +%! assert (fieldnames (x([1, 2], [2:5])), {"d"; "a"; "f"}); +*/ + +octave_map +octave_map::concat (const octave_map& rb, const Array<octave_idx_type>& ra_idx) +{ + if (nfields () == rb.nfields ()) + { + for (const_iterator pa = begin (); pa != end (); pa++) + { + const_iterator pb = rb.seek (key(pa)); + + if (pb == rb.end ()) + { + error ("field name mismatch in structure concatenation"); + break; + } + + contents(pa).insert (rb.contents(pb), ra_idx); + } + } + else + { + dim_vector dv = dims (); + + if (dv.all_zero ()) + *this = rb; + else if (! rb.dims ().all_zero ()) + error ("invalid structure concatenation"); + } + + return *this; +} + +void +octave_map::optimize_dimensions (void) +{ + octave_idx_type nf = nfields (); + + for (octave_idx_type i = 0; i < nf; i++) + { + if (! vals[i].optimize_dimensions (dimensions)) + { + error ("internal error: dimension mismatch across fields in struct"); + break; + } + } + +} + Octave_map::Octave_map (const dim_vector& dv, const Cell& key_vals) : map (), key_list (), dimensions (dv) { @@ -49,6 +1116,16 @@ error ("Octave_map: expecting keys to be cellstr"); } +Octave_map::Octave_map (const octave_map& m) + : map (), key_list (), dimensions (m.dims ()) +{ + for (octave_map::const_iterator p = m.begin (); p != m.end (); p++) + map[m.key (p)] = m.contents (p); + const string_vector mkeys = m.fieldnames (); + for (octave_idx_type i = 0; i < mkeys.numel (); i++) + key_list.push_back (mkeys(i)); +} + Octave_map Octave_map::squeeze (void) const { @@ -70,13 +1147,6 @@ return retval; } -/* -%!# test preservation of keys by squeeze -%!test -%! x(1,1,1,1).d = 10; x(3,5,1,7).a = "b"; x(2,4,1,7).f = 27; -%! assert (fieldnames (squeeze (x)), {"d"; "a"; "f"}); -*/ - Octave_map Octave_map::permute (const Array<int>& vec, bool inv) const { @@ -98,13 +1168,6 @@ return retval; } -/* -%!# test preservation of key order by permute -%!test -%! x(1,1,1,1).d = 10; x(3,5,1,7).a = "b"; x(2,4,1,7).f = 27; -%! assert (fieldnames (permute (x, [3, 4, 1, 2])), {"d"; "a"; "f"}); -*/ - Cell& Octave_map::contents (const std::string& k) { @@ -179,15 +1242,6 @@ return retval; } -/* -%!# test preservation of key order by transpose -%!test -%! x(1,1).d = 10; x(3,5).a = "b"; x(2,4).f = 27; -%! assert (fieldnames (transpose (x)), {"d"; "a"; "f"}); -%! assert (fieldnames (x'), {"d"; "a"; "f"}); -%! assert (fieldnames (x.'), {"d"; "a"; "f"}); -*/ - Octave_map Octave_map::reshape (const dim_vector& new_dims) const { @@ -209,13 +1263,6 @@ return retval; } -/* -%!# test preservation of key order by reshape -%!test -%! x(1,1).d = 10; x(4,6).a = "b"; x(2,4).f = 27; -%! assert (fieldnames (reshape (x, 3, 8)), {"d"; "a"; "f"}); -*/ - void Octave_map::resize (const dim_vector& dv, bool fill) { @@ -286,14 +1333,6 @@ return retval; } -/* -%!# test preservation of key order by concatenation -%!test -%! x(1, 1).d = 10; x(4, 6).a = "b"; x(2, 4).f = 27; -%! y(1, 6).f = 11; y(1, 6).a = "c"; y(1, 6).d = 33; -%! assert (fieldnames ([x; y]), {"d"; "a"; "f"}); -*/ - static bool keys_ok (const Octave_map& a, const Octave_map& b, string_vector& keys) { @@ -526,10 +1565,3 @@ return retval; } - -/* -%!# test preservation of key order by indexing -%!test -%! x(1, 1).d = 10; x(4, 6).a = "b"; x(2, 4).f = 27; -%! assert (fieldnames (x([1, 2], [2:5])), {"d"; "a"; "f"}); -*/
--- a/src/oct-map.h +++ b/src/oct-map.h @@ -2,6 +2,7 @@ Copyright (C) 1994, 1995, 1996, 1997, 2000, 2002, 2003, 2004, 2005, 2006, 2007, 2009 John W. Eaton +Copyright (C) 2010 VZLU Prague This file is part of Octave. @@ -32,6 +33,385 @@ class string_vector; +// A class holding a map field->index. Supports reference-counting. +class OCTINTERP_API +octave_fields +{ + class fields_rep : public std::map<std::string, octave_idx_type> + { + public: + fields_rep (void) : std::map<std::string, octave_idx_type> (), count (1) { } + fields_rep (const fields_rep& other) + : std::map<std::string, octave_idx_type> (other), count (1) { } + + int count; + + private: + fields_rep& operator = (const fields_rep&); // no assignment! + }; + + fields_rep *rep; + + static fields_rep nil_rep; + +public: + + octave_fields (void) : rep (&nil_rep) { rep->count++; } + octave_fields (const string_vector&); + + ~octave_fields (void) + { + if (--rep->count == 0) + delete rep; + } + + void make_unique (void) + { + if (rep->count > 1) + { + --rep->count; + rep = new fields_rep (*rep); + } + } + + octave_fields (const octave_fields& o) : rep (o.rep) { rep->count++; } + + octave_fields& + operator = (const octave_fields& o) + { + o.rep->count++; + if (--rep->count) + delete rep; + rep = o.rep; + + return *this; + } + + // constant iteration support. non-const iteration intentionally unsupported. + + typedef std::map<std::string, octave_idx_type>::const_iterator const_iterator; + typedef const_iterator iterator; + + const_iterator begin (void) const { return rep->begin (); } + const_iterator end (void) const { return rep->end (); } + + std::string key (const_iterator p) const { return p->first; } + octave_idx_type index (const_iterator p) const { return p->second; } + + const_iterator seek (const std::string& k) const + { return rep->find (k); } + + // high-level methods. + + // number of fields. + octave_idx_type nfields (void) const { return rep->size (); } + + // check whether a field exists. + bool isfield (const std::string& name) const; + + // get index of field. return -1 if not exist + octave_idx_type getfield (const std::string& name) const; + // get index of field. add if not exist + octave_idx_type getfield (const std::string& name); + // remove field and return the index. -1 if didn't exist. + octave_idx_type rmfield (const std::string& name); + + // order the fields of this map. creates a permutation + // used to order the fields. + void orderfields (Array<octave_idx_type>& perm); + + // compares two instances for equality up to order of fields. + // returns a permutation needed to bring the fields of *other* + // into the order of *this*. + bool equal_up_to_order (const octave_fields& other, + Array<octave_idx_type>& perm) const; + + bool is_same (const octave_fields& other) const + { return rep == other.rep; } + + // Returns the fields as a vector of strings. + string_vector fieldnames (void) const; + + void clear (void) + { + *this = octave_fields (); + } +}; + + +class OCTINTERP_API +octave_scalar_map +{ + octave_scalar_map (const octave_fields& k) + : keys (k), vals (k.nfields ()) { } + +public: + + octave_scalar_map (void) : keys (), vals () { } + + octave_scalar_map (const string_vector& k) + : keys (k), vals (k.length ()) { } + + octave_scalar_map (const octave_scalar_map& m) + : keys (m.keys), vals(m.vals) { } + + octave_scalar_map& operator = (const octave_scalar_map& m) + { + keys = m.keys; + vals = m.vals; + + return *this; + } + + // iteration support. note that both const and non-const iterators are the + // same. The const/non-const distinction is made by the key & contents method. + typedef octave_fields::const_iterator const_iterator; + typedef const_iterator iterator; + + const_iterator begin (void) const { return keys.begin (); } + const_iterator end (void) const { return keys.end (); } + + const_iterator seek (const std::string& k) const { return keys.seek (k); } + + std::string key (const_iterator p) const + { return keys.key (p); } + octave_idx_type index (const_iterator p) const + { return keys.index (p); } + + const octave_value& contents (const_iterator p) const + { return vals[keys.index (p)]; } + + octave_value& contents (iterator p) + { return vals[keys.index (p)]; } + + const octave_value& contents (octave_idx_type i) const + { return vals[i]; } + + octave_value& contents (octave_idx_type i) + { return vals[i]; } + + // number of fields. + octave_idx_type nfields (void) const { return keys.nfields (); } + + // check whether a field exists. + bool isfield (const std::string& name) const + { return keys.isfield (name); } + + string_vector fieldnames (void) const + { return keys.fieldnames (); } + + // get contents of a given field. empty value if not exist. + octave_value getfield (const std::string& key) const; + + // set contents of a given field. add if not exist. + void setfield (const std::string& key, const octave_value& val); + + // remove a given field. do nothing if not exist. + void rmfield (const std::string& key); + + // return a copy with fields ordered, optionally along with permutation. + octave_scalar_map orderfields (void) const; + octave_scalar_map orderfields (Array<octave_idx_type>& perm) const; + octave_scalar_map orderfields (const octave_scalar_map& other, + Array<octave_idx_type>& perm) const; + + // aka getfield/setfield, but the latter returns a reference. + octave_value contents (const std::string& k) const; + octave_value& contents (const std::string& k); + + void clear (void) + { + keys.clear (); + vals.clear (); + } + + friend class octave_map; + +private: + + octave_fields keys; + std::vector<octave_value> vals; + +}; + +class OCTINTERP_API +octave_map +{ + octave_map (const octave_fields& k) + : keys (k), vals (k.nfields ()), dimensions () { } + +public: + + octave_map (void) : keys (), vals (), dimensions () { } + + octave_map (const dim_vector& dv) : keys (), vals (), dimensions (dv) { } + + octave_map (const string_vector& k) + : keys (k), vals (k.length ()), dimensions (1, 1) { } + + octave_map (const dim_vector& dv, const string_vector& k) + : keys (k), vals (k.length ()), dimensions (dv) { } + + octave_map (const octave_map& m) + : keys (m.keys), vals (m.vals), dimensions (m.dimensions) { } + + octave_map (const octave_scalar_map& m); + + octave_map (const Octave_map& m); + + octave_map& operator = (const octave_map& m) + { + keys = m.keys; + vals = m.vals; + dimensions = m.dimensions; + + return *this; + } + + // iteration support. note that both const and non-const iterators are the + // same. The const/non-const distinction is made by the key & contents method. + typedef octave_fields::const_iterator const_iterator; + typedef const_iterator iterator; + + const_iterator begin (void) const { return keys.begin (); } + const_iterator end (void) const { return keys.end (); } + + const_iterator seek (const std::string& k) const { return keys.seek (k); } + + std::string key (const_iterator p) const + { return keys.key (p); } + octave_idx_type index (const_iterator p) const + { return keys.index (p); } + + const Cell& contents (const_iterator p) const + { return vals[keys.index (p)]; } + + Cell& contents (iterator p) + { return vals[keys.index (p)]; } + + const Cell& contents (octave_idx_type i) const + { return vals[i]; } + + Cell& contents (octave_idx_type i) + { return vals[i]; } + + // number of fields. + octave_idx_type nfields (void) const { return keys.nfields (); } + + // check whether a field exists. + bool isfield (const std::string& name) const + { return keys.isfield (name); } + + string_vector fieldnames (void) const + { return keys.fieldnames (); } + + // get contents of a given field. empty value if not exist. + Cell getfield (const std::string& key) const; + + // set contents of a given field. add if not exist. checks for + // correct dimensions. + void setfield (const std::string& key, const Cell& val); + + // remove a given field. do nothing if not exist. + void rmfield (const std::string& key); + + // return a copy with fields ordered, optionally along with permutation. + octave_map orderfields (void) const; + octave_map orderfields (Array<octave_idx_type>& perm) const; + octave_map orderfields (const octave_map& other, + Array<octave_idx_type>& perm) const; + + // aka getfield/setfield, but the latter returns a reference. + Cell contents (const std::string& k) const; + Cell& contents (const std::string& k); + + void clear (void) + { + keys.clear (); + vals.clear (); + } + + // The Array-like methods. + octave_idx_type numel (void) const { return dimensions.numel (); } + octave_idx_type length (void) const { return numel (); } + + octave_idx_type rows (void) const { return dimensions(0); } + octave_idx_type cols (void) const { return dimensions(1); } + octave_idx_type columns (void) const { return dimensions(1); } + + // Extract a scalar substructure. + octave_scalar_map checkelem (octave_idx_type n) const; + octave_scalar_map checkelem (octave_idx_type i, octave_idx_type j) const; + + octave_scalar_map + checkelem (const Array<octave_idx_type>& ra_idx) const; + + octave_map squeeze (void) const; + + octave_map permute (const Array<int>& vec, bool inv = false) const; + + dim_vector dims (void) const { return dimensions; } + + int ndims (void) const { return dimensions.length (); } + + octave_map transpose (void) const; + + octave_map reshape (const dim_vector& dv) const; + + void resize (const dim_vector& dv); + + static octave_map + cat (int dim, octave_idx_type n, const octave_scalar_map *map_list); + + static octave_map + cat (int dim, octave_idx_type n, const octave_map *map_list); + + octave_map index (const idx_vector& i, bool resize_ok) const; + + octave_map index (const idx_vector& i, const idx_vector& j, + bool resize_ok) const; + + octave_map index (const Array<idx_vector>& ia, + bool resize_ok) const; + + octave_map index (const octave_value_list&, bool resize_ok) const; + + void assign (const idx_vector& i, const octave_map& rhs); + + void assign (const idx_vector& i, const idx_vector& j, const octave_map& rhs); + + void assign (const Array<idx_vector>& ia, const octave_map& rhs); + + void assign (const octave_value_list&, const octave_map& rhs); + + void delete_elements (const idx_vector& i); + + void delete_elements (int dim, const idx_vector& i); + + void delete_elements (const Array<idx_vector>& ia); + + void delete_elements (const octave_value_list&); + + octave_map concat (const octave_map& rb, const Array<octave_idx_type>& ra_idx); + +private: + + octave_fields keys; + std::vector<Cell> vals; + dim_vector dimensions; + + void optimize_dimensions (void); + void extract_scalar (octave_scalar_map& dest, + octave_idx_type index) const; + static void do_cat (int dim, octave_idx_type n, + const octave_scalar_map *map_list, octave_map& retval); + static void do_cat (int dim, octave_idx_type n, + const octave_map *map_list, octave_map& retval); +}; + +// The original Octave_map object. Octave_map and octave_map are convertible to +// each other. + class OCTINTERP_API Octave_map @@ -85,6 +465,8 @@ Octave_map (const Octave_map& m) : map (m.map), key_list (m.key_list), dimensions (m.dimensions) { } + Octave_map (const octave_map& m); + Octave_map& operator = (const Octave_map& m) { if (this != &m)
--- a/src/ov-base.cc +++ b/src/ov-base.cc @@ -885,14 +885,36 @@ return retval; } -Octave_map +octave_map octave_base_value::map_value (void) const { - Octave_map retval; + octave_map retval; gripe_wrong_type_arg ("octave_base_value::map_value()", type_name ()); return retval; } +octave_scalar_map +octave_base_value::scalar_map_value (void) const +{ + octave_map tmp = map_value (); + + if (tmp.numel () == 1) + return tmp.checkelem (0); + else + { + if (! error_state) + error ("invalid conversion of multidimensional struct to scalar struct"); + + return octave_scalar_map (); + } +} + +Octave_map +octave_base_value::old_map_value (void) const +{ + return map_value (); +} + string_vector octave_base_value::map_keys (void) const {
--- a/src/ov-base.h +++ b/src/ov-base.h @@ -42,6 +42,8 @@ class Cell; class Octave_map; +class octave_map; +class octave_scalar_map; class octave_value; class octave_value_list; class octave_stream; @@ -542,7 +544,11 @@ virtual Range range_value (void) const; - virtual Octave_map map_value (void) const; + virtual octave_map map_value (void) const; + + virtual octave_scalar_map scalar_map_value (void) const; + + virtual Octave_map old_map_value (void) const; virtual string_vector map_keys (void) const;
--- a/src/ov-cell.h +++ b/src/ov-cell.h @@ -40,7 +40,6 @@ #include "ov-base-mat.h" #include "ov-typeinfo.h" -class Octave_map; class octave_value_list; class tree_walker;
--- a/src/ov-class.cc +++ b/src/ov-class.cc @@ -261,7 +261,7 @@ Octave_map my_map; - my_map = obvp ? obvp->map_value () : map; + my_map = obvp ? obvp->old_map_value () : map; std::string nm = idx(0).string_value (); @@ -721,7 +721,7 @@ { if (t_rhs.is_object () || t_rhs.is_map ()) { - Octave_map rhs_map = t_rhs.map_value (); + Octave_map rhs_map = t_rhs.old_map_value (); if (! error_state) { @@ -1108,12 +1108,12 @@ octave_value in = new octave_class (*this); octave_value_list tmp = feval ("saveobj", in, 1); if (! error_state) - m = tmp(0).map_value (); + m = tmp(0).old_map_value (); else return false; } else - m = map_value (); + m = old_map_value (); os << "# length: " << m.nfields () << "\n"; @@ -1189,7 +1189,7 @@ octave_value_list tmp = feval ("loadobj", in, 1); if (! error_state) - map = tmp(0).map_value (); + map = tmp(0).old_map_value (); else success = false; } @@ -1238,12 +1238,12 @@ octave_value in = new octave_class (*this); octave_value_list tmp = feval ("saveobj", in, 1); if (! error_state) - m = tmp(0).map_value (); + m = tmp(0).old_map_value (); else return false; } else - m = map_value (); + m = old_map_value (); int32_t len = m.nfields(); os.write (reinterpret_cast<char *> (&len), 4); @@ -1335,7 +1335,7 @@ octave_value_list tmp = feval ("loadobj", in, 1); if (! error_state) - map = tmp(0).map_value (); + map = tmp(0).old_map_value (); else success = false; } @@ -1410,12 +1410,12 @@ octave_value in = new octave_class (*this); octave_value_list tmp = feval ("saveobj", in, 1); if (! error_state) - m = tmp(0).map_value (); + m = tmp(0).old_map_value (); else goto error_cleanup; } else - m = map_value (); + m = old_map_value (); // recursively add each element of the class to this group i = m.begin (); @@ -1576,7 +1576,7 @@ if (! error_state) { - map = tmp(0).map_value (); + map = tmp(0).old_map_value (); retval = true; } else @@ -1624,7 +1624,7 @@ { if (obj.is_object ()) { - Octave_map m = obj.map_value (); + Octave_map m = obj.old_map_value (); field_names = m.keys (); parent_class_names = obj.parent_class_name_list (); @@ -1646,7 +1646,7 @@ { if (nfields () == obj.nfields ()) { - Octave_map obj_map = obj.map_value (); + Octave_map obj_map = obj.old_map_value (); string_vector obj_fnames = obj_map.keys (); string_vector fnames = fields (); @@ -1725,7 +1725,7 @@ if (fcn && fcn->is_class_constructor ()) { - Octave_map m = args(0).map_value (); + Octave_map m = args(0).old_map_value (); if (! error_state) {
--- a/src/ov-class.h +++ b/src/ov-class.h @@ -130,7 +130,9 @@ bool is_object (void) const { return true; } - Octave_map map_value (void) const { return map; } + octave_map map_value (void) const { return map; } + + Octave_map old_map_value (void) const { return map; } string_vector map_keys (void) const;
--- a/src/ov-fcn-inline.cc +++ b/src/ov-fcn-inline.cc @@ -117,7 +117,7 @@ // This function is supplied to allow a Matlab style class structure // to be returned.. -Octave_map +octave_map octave_fcn_inline::map_value (void) const { Octave_map m;
--- a/src/ov-fcn-inline.h +++ b/src/ov-fcn-inline.h @@ -67,7 +67,7 @@ octave_value convert_to_str_internal (bool, bool, char) const; - Octave_map map_value (void) const; + octave_map map_value (void) const; bool save_ascii (std::ostream& os);
--- a/src/ov-struct.cc +++ b/src/ov-struct.cc @@ -51,6 +51,17 @@ DEFINE_OV_TYPEID_FUNCTIONS_AND_DATA(octave_struct, "struct", "struct"); +octave_base_value * +octave_struct::try_narrowing_conversion (void) +{ + octave_base_value *retval = 0; + + if (numel () == 1) + retval = new octave_scalar_struct (map.checkelem (0)); + + return retval; +} + Cell octave_struct::dotref (const octave_value_list& idx, bool auto_add) { @@ -60,7 +71,7 @@ std::string nm = idx(0).string_value (); - Octave_map::const_iterator p = map.seek (nm); + octave_map::const_iterator p = map.seek (nm); if (p != map.end ()) retval = map.contents (p); @@ -131,7 +142,7 @@ } } else - retval(0) = map.index (idx.front ()); + retval(0) = do_index_op (idx.front ()); } break; @@ -197,7 +208,7 @@ } } else - retval = map.index (idx.front (), auto_add); + retval = do_index_op (idx.front (), auto_add); } break; @@ -245,7 +256,7 @@ octave_value retval; if (type.length () > 0 && type[0] == '.' && ! val.is_map ()) - retval = Octave_map (); + retval = octave_map (); else retval = val; @@ -297,11 +308,11 @@ std::string next_type = type.substr (2); Cell tmpc (1, 1); - Octave_map::iterator pkey = map.seek (key); + octave_map::iterator pkey = map.seek (key); if (pkey != map.end ()) { - pkey->second.make_unique (); - tmpc = pkey->second.index (idx.front (), true); + map.contents (pkey).make_unique (); + tmpc = map.contents (pkey).index (idx.front (), true); } // FIXME: better code reuse? cf. octave_cell::subsasgn and the case below. @@ -347,11 +358,11 @@ std::string next_type = type.substr (1); Cell tmpc (1, 1); - Octave_map::iterator pkey = map.seek (key); + octave_map::iterator pkey = map.seek (key); if (pkey != map.end ()) { - pkey->second.make_unique (); - tmpc = pkey->second; + map.contents (pkey).make_unique (); + tmpc = map.contents (pkey); } // FIXME: better code reuse? @@ -420,7 +431,7 @@ tmp_cell = tmp_cell.reshape (didx); - map.assign (idxf, key, tmp_cell); + map.contents (key).assign (idxf, tmp_cell); if (! error_state) { @@ -432,12 +443,12 @@ } else { - const Octave_map& cmap = const_cast<const Octave_map &> (map); + const octave_map& cmap = const_cast<const octave_map &> (map); // cast map to const reference to avoid forced key insertion. if (idxf.all_scalars () || cmap.contents (key).index (idxf, true).numel () == 1) { - map.assign (idxf, key, t_rhs.storable_value ()); + map.contents (key).assign (idxf, Cell (t_rhs.storable_value ())); if (! error_state) { count++; @@ -457,7 +468,7 @@ { if (t_rhs.is_map()) { - Octave_map rhs_map = t_rhs.map_value (); + octave_map rhs_map = t_rhs.map_value (); if (! error_state) { @@ -478,7 +489,7 @@ { if (t_rhs.is_null_value()) { - map.maybe_delete_elements (idx.front()); + map.delete_elements (idx.front()); if (! error_state) { @@ -514,11 +525,14 @@ if (numel () == tmp_cell.numel ()) tmp_cell = tmp_cell.reshape (dims ()); - map.assign (key, tmp_cell); + map.setfield (key, tmp_cell); } else - // Regularize a null matrix if stored into a struct component. - map.assign (key, t_rhs.storable_value ()); + { + Cell tmp_cell(1, 1); + tmp_cell(0) = t_rhs.storable_value (); + map.setfield (key, tmp_cell); + } if (! error_state) { @@ -547,7 +561,7 @@ octave_value octave_struct::do_index_op (const octave_value_list& idx, bool resize_ok) { - // Octave_map handles indexing itself. + // octave_map handles indexing itself. return map.index (idx, resize_ok); } @@ -558,7 +572,7 @@ size_t retval = 0; - for (Octave_map::const_iterator p = map.begin (); p != map.end (); p++) + for (octave_map::const_iterator p = map.begin (); p != map.end (); p++) { std::string key = map.key (p); @@ -606,7 +620,7 @@ increment_indent_level (); } - string_vector key_list = map.keys (); + string_vector key_list = map.fieldnames (); for (octave_idx_type i = 0; i < key_list.length (); i++) { @@ -693,6 +707,996 @@ %!fail("struct('1',2,'3')","struct expects alternating \"field\", VALUE pairs"); */ +bool +octave_struct::save_ascii (std::ostream& os) +{ + octave_map m = map_value (); + + octave_idx_type nf = m.nfields (); + + const dim_vector dv = dims (); + + os << "# ndims: " << dv.length () << "\n"; + + for (int i = 0; i < dv.length (); i++) + os << " " << dv (i); + os << "\n"; + + os << "# length: " << nf << "\n"; + + // Iterating over the list of keys will preserve the order of the + // fields. + string_vector keys = m.fieldnames (); + + for (octave_idx_type i = 0; i < nf; i++) + { + std::string key = keys(i); + + octave_value val = map.contents (key); + + bool b = save_ascii_data (os, val, key, false, 0); + + if (! b) + return os; + } + + return true; +} + +bool +octave_struct::load_ascii (std::istream& is) +{ + octave_idx_type len = 0; + dim_vector dv (1, 1); + bool success = true; + + // KLUGE: earlier Octave versions did not save extra dimensions with struct, + // and as a result did not preserve dimensions for empty structs. + // The default dimensions were 1x1, which we want to preserve. + string_vector keywords(2); + + keywords[0] = "ndims"; + keywords[1] = "length"; + + std::string kw; + + if (extract_keyword (is, keywords, kw, len, true)) + { + if (kw == keywords[0]) + { + int mdims = std::max (static_cast<int> (len), 2); + dv.resize (mdims); + for (int i = 0; i < mdims; i++) + is >> dv(i); + + success = extract_keyword (is, keywords[1], len); + } + } + else + success = false; + + if (success && len >= 0) + { + if (len > 0) + { + octave_map m (dv); + + for (octave_idx_type j = 0; j < len; j++) + { + octave_value t2; + bool dummy; + + // recurse to read cell elements + std::string nm + = read_ascii_data (is, std::string (), dummy, t2, j); + + if (!is) + break; + + Cell tcell = t2.is_cell () ? t2.cell_value () : Cell (t2); + + if (error_state) + { + error ("load: internal error loading struct elements"); + return false; + } + + m.setfield (nm, tcell); + } + + if (is) + map = m; + else + { + error ("load: failed to load structure"); + success = false; + } + } + else if (len == 0 ) + map = octave_map (dv); + else + panic_impossible (); + } + else { + error ("load: failed to extract number of elements in structure"); + success = false; + } + + return success; +} + +bool +octave_struct::save_binary (std::ostream& os, bool& save_as_floats) +{ + octave_map m = map_value (); + + octave_idx_type nf = m.nfields (); + + dim_vector d = dims (); + if (d.length () < 1) + return false; + + // Use negative value for ndims + int32_t di = - d.length(); + os.write (reinterpret_cast<char *> (&di), 4); + for (int i = 0; i < d.length (); i++) + { + di = d(i); + os.write (reinterpret_cast<char *> (&di), 4); + } + + int32_t len = nf; + os.write (reinterpret_cast<char *> (&len), 4); + + // Iterating over the list of keys will preserve the order of the + // fields. + string_vector keys = m.fieldnames (); + + for (octave_idx_type i = 0; i < nf; i++) + { + std::string key = keys(i); + + octave_value val = map.contents (key); + + bool b = save_binary_data (os, val, key, "", 0, save_as_floats); + + if (! b) + return os; + } + + return true; +} + +bool +octave_struct::load_binary (std::istream& is, bool swap, + oct_mach_info::float_format fmt) +{ + bool success = true; + int32_t len; + if (! is.read (reinterpret_cast<char *> (&len), 4)) + return false; + if (swap) + swap_bytes<4> (&len); + + dim_vector dv (1, 1); + + if (len < 0) + { + // We have explicit dimensions. + int mdims = -len; + + int32_t di; + dv.resize (mdims); + + for (int i = 0; i < mdims; i++) + { + if (! is.read (reinterpret_cast<char *> (&di), 4)) + return false; + if (swap) + swap_bytes<4> (&di); + dv(i) = di; + } + + if (! is.read (reinterpret_cast<char *> (&len), 4)) + return false; + if (swap) + swap_bytes<4> (&len); + } + + if (len > 0) + { + octave_map m (dv); + + for (octave_idx_type j = 0; j < len; j++) + { + octave_value t2; + bool dummy; + std::string doc; + + // recurse to read cell elements + std::string nm = read_binary_data (is, swap, fmt, std::string (), + dummy, t2, doc); + + if (!is) + break; + + Cell tcell = t2.is_cell () ? t2.cell_value () : Cell (t2); + + if (error_state) + { + error ("load: internal error loading struct elements"); + return false; + } + + m.setfield (nm, tcell); + } + + if (is) + map = m; + else + { + error ("load: failed to load structure"); + success = false; + } + } + else if (len == 0) + map = octave_map (dv); + else + success = false; + + return success; +} + +#if defined (HAVE_HDF5) + +bool +octave_struct::save_hdf5 (hid_t loc_id, const char *name, bool save_as_floats) +{ + hid_t data_hid = -1; + +#if HAVE_HDF5_18 + data_hid = H5Gcreate (loc_id, name, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); +#else + data_hid = H5Gcreate (loc_id, name, 0); +#endif + if (data_hid < 0) return false; + + // recursively add each element of the structure to this group + octave_map m = map_value (); + + octave_idx_type nf = m.nfields (); + + // Iterating over the list of keys will preserve the order of the + // fields. + string_vector keys = m.fieldnames (); + + for (octave_idx_type i = 0; i < nf; i++) + { + std::string key = keys(i); + + octave_value val = map.contents (key); + + bool retval2 = add_hdf5_data (data_hid, val, key, "", false, + save_as_floats); + + if (! retval2) + break; + } + + H5Gclose (data_hid); + + return true; +} + +bool +octave_struct::load_hdf5 (hid_t loc_id, const char *name) +{ + bool retval = false; + + hdf5_callback_data dsub; + + herr_t retval2 = 0; + octave_map m (dim_vector (1, 1)); + int current_item = 0; + hsize_t num_obj = 0; +#if HAVE_HDF5_18 + hid_t group_id = H5Gopen (loc_id, name, H5P_DEFAULT); +#else + hid_t group_id = H5Gopen (loc_id, name); +#endif + H5Gget_num_objs (group_id, &num_obj); + H5Gclose (group_id); + + // FIXME -- fields appear to be sorted alphabetically on loading. + // Why is that happening? + + while (current_item < static_cast<int> (num_obj) + && (retval2 = H5Giterate (loc_id, name, ¤t_item, + hdf5_read_next_data, &dsub)) > 0) + { + octave_value t2 = dsub.tc; + + Cell tcell = t2.is_cell () ? t2.cell_value () : Cell (t2); + + if (error_state) + { + error ("load: internal error loading struct elements"); + return false; + } + + m.setfield (dsub.name, tcell); + + } + + if (retval2 >= 0) + { + map = m; + retval = true; + } + + return retval; +} + +#endif + +mxArray * +octave_struct::as_mxArray (void) const +{ + int nf = nfields (); + string_vector kv = map_keys (); + + OCTAVE_LOCAL_BUFFER (const char *, f, nf); + + for (int i = 0; i < nf; i++) + f[i] = kv[i].c_str (); + + mxArray *retval = new mxArray (dims (), nf, f); + + mxArray **elts = static_cast<mxArray **> (retval->get_data ()); + + mwSize nel = numel (); + + mwSize ntot = nf * nel; + + for (int i = 0; i < nf; i++) + { + Cell c = map.contents (kv[i]); + + const octave_value *p = c.data (); + + mwIndex k = 0; + for (mwIndex j = i; j < ntot; j += nf) + elts[j] = new mxArray (p[k++]); + } + + return retval; +} + +DEFINE_OCTAVE_ALLOCATOR(octave_scalar_struct); + +DEFINE_OV_TYPEID_FUNCTIONS_AND_DATA(octave_scalar_struct, "scalar struct", "struct"); + +octave_value +octave_scalar_struct::dotref (const octave_value_list& idx, bool auto_add) +{ + assert (idx.length () == 1); + + std::string nm = idx(0).string_value (); + + octave_value retval = map.getfield (nm); + + if (! auto_add && retval.is_undefined ()) + error ("structure has no member `%s'", nm.c_str ()); + + return retval; +} + +octave_value +octave_scalar_struct::subsref (const std::string& type, + const std::list<octave_value_list>& idx) +{ + octave_value retval; + + if (type[0] == '.') + { + int skip = 1; + + retval = dotref (idx.front ()); + + if (idx.size () > 1) + retval = retval.next_subsref (type, idx, skip); + } + else + retval = to_array ().subsref (type, idx); + + return retval; +} + +octave_value_list +octave_scalar_struct::subsref (const std::string& type, + const std::list<octave_value_list>& idx, + int nargout) +{ + octave_value_list retval; + + if (type[0] == '.') + { + int skip = 1; + + retval(0) = dotref (idx.front ()); + + if (idx.size () > 1) + retval = retval(0).next_subsref (nargout, type, idx, skip); + } + else + retval = to_array ().subsref (type, idx, nargout); + + return retval; +} + +octave_value +octave_scalar_struct::subsref (const std::string& type, + const std::list<octave_value_list>& idx, + bool auto_add) +{ + octave_value retval; + + if (type[0] == '.') + { + int skip = 1; + + retval = dotref (idx.front (), auto_add); + + if (idx.size () > 1) + retval = retval.next_subsref (auto_add, type, idx, skip); + } + else + retval = to_array ().subsref (type, idx, auto_add); + + return retval; +} + +/* +%!test +%! x(1).a.a = 1; x(2).a.a = 2; +%! assert (size (x), [1, 2]); +%! assert (x(1).a.a, 1); +%! assert (x(2).a.a, 2); +*/ + +octave_value +octave_scalar_struct::numeric_conv (const octave_value& val, + const std::string& type) +{ + octave_value retval; + + if (type.length () > 0 && type[0] == '.' && ! val.is_map ()) + retval = octave_map (); + else + retval = val; + + return retval; +} + +octave_value +octave_scalar_struct::subsasgn (const std::string& type, + const std::list<octave_value_list>& idx, + const octave_value& rhs) +{ + octave_value retval; + + if (idx.front ().empty ()) + { + error ("missing index in indexed assignment"); + return retval; + } + + if (type[0] == '.') + { + int n = type.length (); + + octave_value t_rhs = rhs; + + octave_value_list key_idx = idx.front (); + + assert (key_idx.length () == 1); + + std::string key = key_idx(0).string_value (); + + if (n > 1) + { + std::list<octave_value_list> next_idx (idx); + + next_idx.erase (next_idx.begin ()); + + std::string next_type = type.substr (1); + + octave_value tmp; + octave_map::iterator pkey = map.seek (key); + if (pkey != map.end ()) + { + map.contents (pkey).make_unique (); + tmp = map.contents (pkey); + } + + if (! error_state) + { + if (! tmp.is_defined () || tmp.is_zero_by_zero ()) + { + tmp = octave_value::empty_conv (next_type, rhs); + tmp.make_unique (); // probably a no-op. + } + else + // optimization: ignore the copy still stored inside our map. + tmp.make_unique (1); + + if (! error_state) + t_rhs = tmp.subsasgn (next_type, next_idx, rhs); + } + } + + if (! error_state) + map.setfield (key, t_rhs.storable_value ()); + else + gripe_failed_assignment (); + } + else + { + // Forward this case to octave_struct. + octave_value tmp (new octave_struct (octave_map (map))); + retval = tmp.subsasgn (type, idx, rhs); + } + + return retval; +} + +octave_value +octave_scalar_struct::do_index_op (const octave_value_list& idx, bool resize_ok) +{ + // octave_map handles indexing itself. + return octave_map (map).index (idx, resize_ok); +} + +size_t +octave_scalar_struct::byte_size (void) const +{ + // Neglect the size of the fieldnames. + + size_t retval = 0; + + for (octave_map::const_iterator p = map.begin (); p != map.end (); p++) + { + std::string key = map.key (p); + + octave_value val = octave_value (map.contents (p)); + + retval += val.byte_size (); + } + + return retval; +} + +void +octave_scalar_struct::print (std::ostream& os, bool) const +{ + print_raw (os); +} + +void +octave_scalar_struct::print_raw (std::ostream& os, bool) const +{ + unwind_protect frame; + + frame.protect_var (Vstruct_levels_to_print); + + if (Vstruct_levels_to_print >= 0) + { + bool print_keys_only = Vstruct_levels_to_print-- == 0; + + indent (os); + os << "{"; + newline (os); + + increment_indent_level (); + + octave_idx_type n = 1; + + if (n != 1 || print_keys_only) + { + indent (os); + dim_vector dv = dims (); + os << dv.str () << " struct array containing the fields:"; + newline (os); + newline (os); + + increment_indent_level (); + } + + string_vector key_list = map.fieldnames (); + + for (octave_idx_type i = 0; i < key_list.length (); i++) + { + std::string key = key_list[i]; + + Cell val = map.contents (key); + + octave_value tmp = (n == 1) ? val(0) : octave_value (val, true); + + if (n != 1 || print_keys_only) + { + indent (os); + os << key; + if (n == 1) + { + dim_vector dv = tmp.dims (); + os << ": " << dv.str () << " " << tmp.type_name (); + } + newline (os); + } + else + tmp.print_with_name (os, key); + } + + if (n != 1 || print_keys_only) + decrement_indent_level (); + + decrement_indent_level (); + + indent (os); + os << "}"; + newline (os); + } + else + { + indent (os); + os << "<structure>"; + newline (os); + } +} + +bool +octave_scalar_struct::print_name_tag (std::ostream& os, const std::string& name) const +{ + bool retval = false; + + indent (os); + + if (Vstruct_levels_to_print < 0) + os << name << " = "; + else + { + os << name << " ="; + newline (os); + retval = true; + } + + return retval; +} + +bool +octave_scalar_struct::save_ascii (std::ostream& os) +{ + octave_map m = map_value (); + + octave_idx_type nf = m.nfields (); + + const dim_vector dv = dims (); + + os << "# ndims: " << dv.length () << "\n"; + + for (int i = 0; i < dv.length (); i++) + os << " " << dv (i); + os << "\n"; + + os << "# length: " << nf << "\n"; + + // Iterating over the list of keys will preserve the order of the + // fields. + string_vector keys = m.fieldnames (); + + for (octave_idx_type i = 0; i < nf; i++) + { + std::string key = keys(i); + + octave_value val = map.contents (key); + + bool b = save_ascii_data (os, val, key, false, 0); + + if (! b) + return os; + } + + return true; +} + +bool +octave_scalar_struct::load_ascii (std::istream& is) +{ + bool success = true; + octave_idx_type len = 0; + + if (extract_keyword (is, "length", len, true) && len >= 0) + { + if (len > 0) + { + octave_scalar_map m; + + for (octave_idx_type j = 0; j < len; j++) + { + octave_value t2; + bool dummy; + + // recurse to read cell elements + std::string nm + = read_ascii_data (is, std::string (), dummy, t2, j); + + if (!is) + break; + + if (error_state) + { + error ("load: internal error loading struct elements"); + return false; + } + + m.setfield (nm, t2); + } + + if (is) + map = m; + else + { + error ("load: failed to load structure"); + success = false; + } + } + else if (len == 0) + map = octave_scalar_map (); + else + panic_impossible (); + } + else { + error ("load: failed to extract number of elements in structure"); + success = false; + } + + return success; +} + +bool +octave_scalar_struct::save_binary (std::ostream& os, bool& save_as_floats) +{ + octave_map m = map_value (); + + octave_idx_type nf = m.nfields (); + + int32_t len = nf; + os.write (reinterpret_cast<char *> (&len), 4); + + // Iterating over the list of keys will preserve the order of the + // fields. + string_vector keys = m.fieldnames (); + + for (octave_idx_type i = 0; i < nf; i++) + { + std::string key = keys(i); + + octave_value val = map.contents (key); + + bool b = save_binary_data (os, val, key, "", 0, save_as_floats); + + if (! b) + return os; + } + + return true; +} + +bool +octave_scalar_struct::load_binary (std::istream& is, bool swap, + oct_mach_info::float_format fmt) +{ + bool success = true; + int32_t len; + if (! is.read (reinterpret_cast<char *> (&len), 4)) + return false; + if (swap) + swap_bytes<4> (&len); + + dim_vector dv (1, 1); + + if (len > 0) + { + octave_scalar_map m; + + for (octave_idx_type j = 0; j < len; j++) + { + octave_value t2; + bool dummy; + std::string doc; + + // recurse to read cell elements + std::string nm = read_binary_data (is, swap, fmt, std::string (), + dummy, t2, doc); + + if (!is) + break; + + if (error_state) + { + error ("load: internal error loading struct elements"); + return false; + } + + m.setfield (nm, t2); + } + + if (is) + map = m; + else + { + error ("load: failed to load structure"); + success = false; + } + } + else if (len == 0) + map = octave_scalar_map (); + else + success = false; + + return success; +} + +#if defined (HAVE_HDF5) + +bool +octave_scalar_struct::save_hdf5 (hid_t loc_id, const char *name, bool save_as_floats) +{ + hid_t data_hid = -1; + +#if HAVE_HDF5_18 + data_hid = H5Gcreate (loc_id, name, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); +#else + data_hid = H5Gcreate (loc_id, name, 0); +#endif + if (data_hid < 0) return false; + + // recursively add each element of the structure to this group + octave_scalar_map m = scalar_map_value (); + + octave_idx_type nf = m.nfields (); + + // Iterating over the list of keys will preserve the order of the + // fields. + string_vector keys = m.fieldnames (); + + for (octave_idx_type i = 0; i < nf; i++) + { + std::string key = keys(i); + + octave_value val = map.contents (key); + + bool retval2 = add_hdf5_data (data_hid, val, key, "", false, + save_as_floats); + + if (! retval2) + break; + } + + H5Gclose (data_hid); + + return true; +} + +bool +octave_scalar_struct::load_hdf5 (hid_t loc_id, const char *name) +{ + bool retval = false; + + hdf5_callback_data dsub; + + herr_t retval2 = 0; + octave_scalar_map m; + int current_item = 0; + hsize_t num_obj = 0; +#if HAVE_HDF5_18 + hid_t group_id = H5Gopen (loc_id, name, H5P_DEFAULT); +#else + hid_t group_id = H5Gopen (loc_id, name); +#endif + H5Gget_num_objs (group_id, &num_obj); + H5Gclose (group_id); + + // FIXME -- fields appear to be sorted alphabetically on loading. + // Why is that happening? + + while (current_item < static_cast<int> (num_obj) + && (retval2 = H5Giterate (loc_id, name, ¤t_item, + hdf5_read_next_data, &dsub)) > 0) + { + octave_value t2 = dsub.tc; + + if (error_state) + { + error ("load: internal error loading struct elements"); + return false; + } + + m.setfield (dsub.name, t2); + + } + + if (retval2 >= 0) + { + map = m; + retval = true; + } + + return retval; +} + +#endif + +mxArray * +octave_scalar_struct::as_mxArray (void) const +{ + int nf = nfields (); + string_vector kv = map_keys (); + + OCTAVE_LOCAL_BUFFER (const char *, f, nf); + + for (int i = 0; i < nf; i++) + f[i] = kv[i].c_str (); + + mxArray *retval = new mxArray (dims (), nf, f); + + mxArray **elts = static_cast<mxArray **> (retval->get_data ()); + + mwSize nel = numel (); + + mwSize ntot = nf * nel; + + for (int i = 0; i < nf; i++) + { + Cell c = map.contents (kv[i]); + + const octave_value *p = c.data (); + + mwIndex k = 0; + for (mwIndex j = i; j < ntot; j += nf) + elts[j] = new mxArray (p[k++]); + } + + return retval; +} + + +octave_value +octave_scalar_struct::to_array (void) +{ + return new octave_struct (octave_map (map)); +} + +/* +%!shared x +%! x(1).a=1; x(2).a=2; x(1).b=3; x(2).b=3; +%!assert(struct('a',1,'b',3),x(1)) +%!assert(isempty(x([]))) +%!assert(isempty(struct('a',{},'b',{}))) +%!assert(struct('a',{1,2},'b',{3,3}),x) +%!assert(struct('a',{1,2},'b',3),x) +%!assert(struct('a',{1,2},'b',{3}),x) +%!assert(struct('b',3,'a',{1,2}),x) +%!assert(struct('b',{3},'a',{1,2}),x) +%!test x=struct([]); +%!assert(size(x),[0,0]); +%!assert(isstruct(x)); +%!assert(isempty(fieldnames(x))); +%!fail("struct('a',{1,2},'b',{1,2,3})","dimensions of parameter 2 do not match those of parameter 4") +%!fail("struct(1,2,3,4)","struct expects alternating \"field\", VALUE pairs"); +%!fail("struct('1',2,'3')","struct expects alternating \"field\", VALUE pairs"); +*/ + DEFUN (struct, args, , "-*- texinfo -*-\n\ @deftypefn {Built-in Function} {} struct (\"field\", @var{value}, \"field\", @var{value}, @dots{})\n\ @@ -722,7 +1726,7 @@ if (nargin == 1 && args(0).is_object ()) { - Octave_map m = args(0).map_value (); + octave_map m = args(0).map_value (); retval = octave_value (new octave_struct (m)); return retval; @@ -736,12 +1740,12 @@ if (nargin == 2) { if (args(1).is_cellstr ()) - retval = Octave_map (args(0).dims (), args(1).cell_value ()); + retval = octave_map (args(0).dims (), args(1).cellstr_value ()); else error ("struct: expecting cell array of field names as second argument"); } else - retval = Octave_map (args(0).dims ()); + retval = octave_map (args(0).dims ()); return retval; } @@ -788,7 +1792,7 @@ // Create the return value. - Octave_map map (dims); + octave_map map (dims); for (int i = 0; i < nargin; i+= 2) { @@ -819,12 +1823,12 @@ return retval; if (scalar (c.dims ())) - map.assign (key, Cell (dims, c(0))); + map.setfield (key, Cell (dims, c(0))); else - map.assign (key, c); + map.setfield (key, c); } else - map.assign (key, Cell (dims, args(i+1))); + map.setfield (key, Cell (dims, args(i+1))); if (error_state) return retval; @@ -868,14 +1872,14 @@ if (arg.is_map () || arg.is_object ()) { - Octave_map m = arg.map_value (); - - string_vector keys = m.keys (); + octave_map m = arg.map_value (); + + string_vector keys = m.fieldnames (); if (keys.length () == 0) retval = Cell (0, 1); else - retval = Cell (m.keys ()); + retval = Cell (keys); } else gripe_wrong_type_arg ("fieldnames", args(0)); @@ -911,7 +1915,7 @@ if (args(0).is_map ()) { - Octave_map m = args(0).map_value (); + octave_map m = args(0).map_value (); // FIXME -- should this work for all types that can do // structure reference operations? @@ -920,12 +1924,12 @@ { std::string key = args(1).string_value (); - retval = m.contains (key) != 0; + retval = m.isfield (key); } else if (args(1).is_cell ()) { Cell c = args(1).cell_value (); - boolMatrix bm (c.dims ()); + boolNDArray bm (c.dims ()); octave_idx_type n = bm.numel (); for (octave_idx_type i = 0; i < n; i++) @@ -934,7 +1938,7 @@ { std::string key = c(i).string_value (); - bm(i) = m.contains (key) != 0; + bm(i) = m.isfield (key); } else bm(i) = false; @@ -1133,7 +2137,7 @@ // All initializing is done, we can start moving // values. - Octave_map map; + octave_map map; // If field is a cell array then we use all // elements in array, on the other hand when @@ -1207,7 +2211,7 @@ map.reshape (value_dv); - map.assign (field_str, c_value); + map.setfield (field_str, c_value); } if (! error_state) @@ -1260,7 +2264,7 @@ if (nargin == 2) { - Octave_map m = args(0).map_value (); + octave_map m = args(0).map_value (); octave_value_list fval = Fcellstr (args(1), 1); @@ -1272,8 +2276,8 @@ { std::string key = fcell(i).string_value (); - if (m.contains (key)) - m.del (key); + if (m.isfield (key)) + m.rmfield (key); else { error ("rmfield: structure does not contain field %s", @@ -1302,367 +2306,3 @@ %! assert (size (y), [1, 6]); */ -bool -octave_struct::save_ascii (std::ostream& os) -{ - Octave_map m = map_value (); - - octave_idx_type nf = m.nfields (); - - const dim_vector dv = dims (); - - os << "# ndims: " << dv.length () << "\n"; - - for (int i = 0; i < dv.length (); i++) - os << " " << dv (i); - os << "\n"; - - os << "# length: " << nf << "\n"; - - // Iterating over the list of keys will preserve the order of the - // fields. - string_vector keys = m.keys (); - - for (octave_idx_type i = 0; i < nf; i++) - { - std::string key = keys(i); - - octave_value val = map.contents (key); - - bool b = save_ascii_data (os, val, key, false, 0); - - if (! b) - return os; - } - - return true; -} - -bool -octave_struct::load_ascii (std::istream& is) -{ - octave_idx_type len = 0; - dim_vector dv (1, 1); - bool success = true; - - // KLUGE: earlier Octave versions did not save extra dimensions with struct, - // and as a result did not preserve dimensions for empty structs. - // The default dimensions were 1x1, which we want to preserve. - string_vector keywords(2); - - keywords[0] = "ndims"; - keywords[1] = "length"; - - std::string kw; - - if (extract_keyword (is, keywords, kw, len, true)) - { - if (kw == keywords[0]) - { - int mdims = std::max (static_cast<int> (len), 2); - dv.resize (mdims); - for (int i = 0; i < mdims; i++) - is >> dv(i); - - success = extract_keyword (is, keywords[1], len); - } - } - else - success = false; - - if (success && len >= 0) - { - if (len > 0) - { - Octave_map m (dv); - - for (octave_idx_type j = 0; j < len; j++) - { - octave_value t2; - bool dummy; - - // recurse to read cell elements - std::string nm - = read_ascii_data (is, std::string (), dummy, t2, j); - - if (!is) - break; - - Cell tcell = t2.is_cell () ? t2.cell_value () : Cell (t2); - - if (error_state) - { - error ("load: internal error loading struct elements"); - return false; - } - - m.assign (nm, tcell); - } - - if (is) - map = m; - else - { - error ("load: failed to load structure"); - success = false; - } - } - else if (len == 0 ) - map = Octave_map (dv); - else - panic_impossible (); - } - else { - error ("load: failed to extract number of elements in structure"); - success = false; - } - - return success; -} - -bool -octave_struct::save_binary (std::ostream& os, bool& save_as_floats) -{ - Octave_map m = map_value (); - - octave_idx_type nf = m.nfields (); - - dim_vector d = dims (); - if (d.length () < 1) - return false; - - // Use negative value for ndims - int32_t di = - d.length(); - os.write (reinterpret_cast<char *> (&di), 4); - for (int i = 0; i < d.length (); i++) - { - di = d(i); - os.write (reinterpret_cast<char *> (&di), 4); - } - - int32_t len = nf; - os.write (reinterpret_cast<char *> (&len), 4); - - // Iterating over the list of keys will preserve the order of the - // fields. - string_vector keys = m.keys (); - - for (octave_idx_type i = 0; i < nf; i++) - { - std::string key = keys(i); - - octave_value val = map.contents (key); - - bool b = save_binary_data (os, val, key, "", 0, save_as_floats); - - if (! b) - return os; - } - - return true; -} - -bool -octave_struct::load_binary (std::istream& is, bool swap, - oct_mach_info::float_format fmt) -{ - bool success = true; - int32_t len; - if (! is.read (reinterpret_cast<char *> (&len), 4)) - return false; - if (swap) - swap_bytes<4> (&len); - - dim_vector dv (1, 1); - - if (len < 0) - { - // We have explicit dimensions. - int mdims = -len; - - int32_t di; - dv.resize (mdims); - - for (int i = 0; i < mdims; i++) - { - if (! is.read (reinterpret_cast<char *> (&di), 4)) - return false; - if (swap) - swap_bytes<4> (&di); - dv(i) = di; - } - - if (! is.read (reinterpret_cast<char *> (&len), 4)) - return false; - if (swap) - swap_bytes<4> (&len); - } - - if (len > 0) - { - Octave_map m (dv); - - for (octave_idx_type j = 0; j < len; j++) - { - octave_value t2; - bool dummy; - std::string doc; - - // recurse to read cell elements - std::string nm = read_binary_data (is, swap, fmt, std::string (), - dummy, t2, doc); - - if (!is) - break; - - Cell tcell = t2.is_cell () ? t2.cell_value () : Cell (t2); - - if (error_state) - { - error ("load: internal error loading struct elements"); - return false; - } - - m.assign (nm, tcell); - } - - if (is) - map = m; - else - { - error ("load: failed to load structure"); - success = false; - } - } - else if (len == 0) - map = Octave_map (dv); - else - success = false; - - return success; -} - -#if defined (HAVE_HDF5) - -bool -octave_struct::save_hdf5 (hid_t loc_id, const char *name, bool save_as_floats) -{ - hid_t data_hid = -1; - -#if HAVE_HDF5_18 - data_hid = H5Gcreate (loc_id, name, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); -#else - data_hid = H5Gcreate (loc_id, name, 0); -#endif - if (data_hid < 0) return false; - - // recursively add each element of the structure to this group - Octave_map m = map_value (); - - octave_idx_type nf = m.nfields (); - - // Iterating over the list of keys will preserve the order of the - // fields. - string_vector keys = m.keys (); - - for (octave_idx_type i = 0; i < nf; i++) - { - std::string key = keys(i); - - octave_value val = map.contents (key); - - bool retval2 = add_hdf5_data (data_hid, val, key, "", false, - save_as_floats); - - if (! retval2) - break; - } - - H5Gclose (data_hid); - - return true; -} - -bool -octave_struct::load_hdf5 (hid_t loc_id, const char *name) -{ - bool retval = false; - - hdf5_callback_data dsub; - - herr_t retval2 = 0; - Octave_map m (dim_vector (1, 1)); - int current_item = 0; - hsize_t num_obj = 0; -#if HAVE_HDF5_18 - hid_t group_id = H5Gopen (loc_id, name, H5P_DEFAULT); -#else - hid_t group_id = H5Gopen (loc_id, name); -#endif - H5Gget_num_objs (group_id, &num_obj); - H5Gclose (group_id); - - // FIXME -- fields appear to be sorted alphabetically on loading. - // Why is that happening? - - while (current_item < static_cast<int> (num_obj) - && (retval2 = H5Giterate (loc_id, name, ¤t_item, - hdf5_read_next_data, &dsub)) > 0) - { - octave_value t2 = dsub.tc; - - Cell tcell = t2.is_cell () ? t2.cell_value () : Cell (t2); - - if (error_state) - { - error ("load: internal error loading struct elements"); - return false; - } - - m.assign (dsub.name, tcell); - - } - - if (retval2 >= 0) - { - map = m; - retval = true; - } - - return retval; -} - -#endif - -mxArray * -octave_struct::as_mxArray (void) const -{ - int nf = nfields (); - string_vector kv = map_keys (); - - OCTAVE_LOCAL_BUFFER (const char *, f, nf); - - for (int i = 0; i < nf; i++) - f[i] = kv[i].c_str (); - - mxArray *retval = new mxArray (dims (), nf, f); - - mxArray **elts = static_cast<mxArray **> (retval->get_data ()); - - mwSize nel = numel (); - - mwSize ntot = nf * nel; - - for (int i = 0; i < nf; i++) - { - Cell c = map.contents (kv[i]); - - const octave_value *p = c.data (); - - mwIndex k = 0; - for (mwIndex j = i; j < ntot; j += nf) - elts[j] = new mxArray (p[k++]); - } - - return retval; -}
--- a/src/ov-struct.h +++ b/src/ov-struct.h @@ -52,6 +52,9 @@ octave_struct (void) : octave_base_value () { } + octave_struct (const octave_map& m) + : octave_base_value (), map (m) { } + octave_struct (const Octave_map& m) : octave_base_value (), map (m) { } @@ -63,6 +66,8 @@ octave_base_value *clone (void) const { return new octave_struct (*this); } octave_base_value *empty_clone (void) const { return new octave_struct (); } + octave_base_value *try_narrowing_conversion (void); + Cell dotref (const octave_value_list& idx, bool auto_add = false); octave_value subsref (const std::string& type, @@ -102,8 +107,7 @@ // of elements is numel () * nfields (). octave_idx_type numel (void) const { - dim_vector dv = dims (); - return dv.numel (); + return map.numel (); } octave_idx_type nfields (void) const { return map.nfields (); } @@ -112,7 +116,7 @@ { return map.reshape (new_dims); } octave_value resize (const dim_vector& dv, bool = false) const - { Octave_map tmap = map; tmap.resize (dv); return tmap; } + { octave_map tmap = map; tmap.resize (dv); return tmap; } bool is_defined (void) const { return true; } @@ -122,9 +126,9 @@ builtin_type_t builtin_type (void) const { return btyp_struct; } - Octave_map map_value (void) const { return map; } + octave_map map_value (void) const { return map; } - string_vector map_keys (void) const { return map.keys (); } + string_vector map_keys (void) const { return map.fieldnames (); } void print (std::ostream& os, bool pr_as_read_syntax = false) const; @@ -152,7 +156,7 @@ protected: // The associative array used to manage the structure data. - Octave_map map; + octave_map map; private: @@ -161,4 +165,121 @@ DECLARE_OV_TYPEID_FUNCTIONS_AND_DATA }; +class +octave_scalar_struct : public octave_base_value +{ +public: + + octave_scalar_struct (void) + : octave_base_value () { } + + octave_scalar_struct (const octave_scalar_map& m) + : octave_base_value (), map (m) { } + + octave_scalar_struct (const octave_scalar_struct& s) + : octave_base_value (), map (s.map) { } + + ~octave_scalar_struct (void) { } + + octave_base_value *clone (void) const { return new octave_scalar_struct (*this); } + octave_base_value *empty_clone (void) const { return new octave_scalar_struct (); } + + octave_value dotref (const octave_value_list& idx, bool auto_add = false); + + octave_value subsref (const std::string& type, + const std::list<octave_value_list>& idx); + + octave_value_list subsref (const std::string& type, + const std::list<octave_value_list>& idx, int); + + + octave_value subsref (const std::string& type, + const std::list<octave_value_list>& idx, + bool auto_add); + + static octave_value numeric_conv (const octave_value& val, + const std::string& type); + + octave_value subsasgn (const std::string& type, + const std::list<octave_value_list>& idx, + const octave_value& rhs); + + octave_value squeeze (void) const { return map; } + + octave_value permute (const Array<int>& vec, bool inv = false) const + { return octave_map (map).permute (vec, inv); } + + octave_value do_index_op (const octave_value_list& idx, + bool resize_ok = false); + + dim_vector dims (void) const { static dim_vector dv (1, 1); return dv; } + + size_t byte_size (void) const; + + // This is the number of elements in each field. The total number + // of elements is numel () * nfields (). + octave_idx_type numel (void) const + { + return 1; + } + + octave_idx_type nfields (void) const { return map.nfields (); } + + octave_value reshape (const dim_vector& new_dims) const + { return octave_map (map).reshape (new_dims); } + + octave_value resize (const dim_vector& dv, bool = false) const + { octave_map tmap = map; tmap.resize (dv); return tmap; } + + bool is_defined (void) const { return true; } + + bool is_constant (void) const { return true; } + + bool is_map (void) const { return true; } + + builtin_type_t builtin_type (void) const { return btyp_struct; } + + octave_map map_value (void) const { return map; } + + octave_scalar_map scalar_map_value (void) const { return map; } + + string_vector map_keys (void) const { return map.fieldnames (); } + + void print (std::ostream& os, bool pr_as_read_syntax = false) const; + + void print_raw (std::ostream& os, bool pr_as_read_syntax = false) const; + + bool print_name_tag (std::ostream& os, const std::string& name) const; + + bool save_ascii (std::ostream& os); + + bool load_ascii (std::istream& is); + + bool save_binary (std::ostream& os, bool& save_as_floats); + + bool load_binary (std::istream& is, bool swap, + oct_mach_info::float_format fmt); + +#if defined (HAVE_HDF5) + bool save_hdf5 (hid_t loc_id, const char *name, bool save_as_floats); + + bool load_hdf5 (hid_t loc_id, const char *name); #endif + + mxArray *as_mxArray (void) const; + +protected: + + // The associative array used to manage the structure data. + octave_scalar_map map; + +private: + + octave_value to_array (void); + + DECLARE_OCTAVE_ALLOCATOR + + DECLARE_OV_TYPEID_FUNCTIONS_AND_DATA +}; + +#endif
--- a/src/ov.cc +++ b/src/ov.cc @@ -1116,9 +1116,21 @@ maybe_mutate (); } +octave_value::octave_value (const octave_map& m) + : rep (new octave_struct (m)) +{ + maybe_mutate (); +} + +octave_value::octave_value (const octave_scalar_map& m) + : rep (new octave_scalar_struct (m)) +{ +} + octave_value::octave_value (const Octave_map& m) : rep (new octave_struct (m)) { + maybe_mutate (); } octave_value::octave_value (const Octave_map& m, const std::string& id) @@ -1431,12 +1443,24 @@ #endif } -Octave_map +octave_map octave_value::map_value (void) const { return rep->map_value (); } +octave_scalar_map +octave_value::scalar_map_value (void) const +{ + return rep->scalar_map_value (); +} + +Octave_map +octave_value::old_map_value (void) const +{ + return rep->old_map_value (); +} + octave_function * octave_value::function_value (bool silent) const {
--- a/src/ov.h +++ b/src/ov.h @@ -45,6 +45,8 @@ #include "oct-sort.h" class Cell; +class octave_map; +class octave_scalar_map; class Octave_map; class octave_stream; class octave_function; @@ -275,6 +277,8 @@ octave_value (const idx_vector& idx, bool lazy = true); octave_value (double base, double limit, double inc); octave_value (const Range& r); + octave_value (const octave_map& m); + octave_value (const octave_scalar_map& m); octave_value (const Octave_map& m); octave_value (const Octave_map& m, const std::string& id); octave_value (const octave_value_list& m, bool = false); @@ -840,7 +844,11 @@ Range range_value (void) const { return rep->range_value (); } - Octave_map map_value (void) const; + octave_map map_value (void) const; + + octave_scalar_map scalar_map_value (void) const; + + Octave_map old_map_value (void) const; string_vector map_keys (void) const { return rep->map_keys (); }