GNU Octave  6.2.0
A high-level interpreted language, primarily intended for numerical computations, mostly compatible with Matlab
ov-struct.cc
Go to the documentation of this file.
1 ////////////////////////////////////////////////////////////////////////
2 //
3 // Copyright (C) 1996-2021 The Octave Project Developers
4 //
5 // See the file COPYRIGHT.md in the top-level directory of this
6 // distribution or <https://octave.org/copyright/>.
7 //
8 // This file is part of Octave.
9 //
10 // Octave is free software: you can redistribute it and/or modify it
11 // under the terms of the GNU General Public License as published by
12 // the Free Software Foundation, either version 3 of the License, or
13 // (at your option) any later version.
14 //
15 // Octave is distributed in the hope that it will be useful, but
16 // WITHOUT ANY WARRANTY; without even the implied warranty of
17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 // GNU General Public License for more details.
19 //
20 // You should have received a copy of the GNU General Public License
21 // along with Octave; see the file COPYING. If not, see
22 // <https://www.gnu.org/licenses/>.
23 //
24 ////////////////////////////////////////////////////////////////////////
25 
26 #if defined (HAVE_CONFIG_H)
27 # include "config.h"
28 #endif
29 
30 #include <istream>
31 #include <ostream>
32 
33 #include "Cell.h"
34 #include "builtin-defun-decls.h"
35 #include "defun.h"
36 #include "error.h"
37 #include "errwarn.h"
38 #include "mxarray.h"
39 #include "oct-lvalue.h"
40 #include "oct-hdf5.h"
41 #include "ov-struct.h"
42 #include "unwind-prot.h"
43 #include "utils.h"
44 #include "variables.h"
45 
46 #include "Array-util.h"
47 #include "oct-locbuf.h"
48 
49 #include "byte-swap.h"
50 #include "ls-oct-text.h"
51 #include "ls-oct-binary.h"
52 #include "ls-hdf5.h"
53 #include "ls-utils.h"
54 #include "pr-output.h"
55 
56 
58 
59 // How many levels of structure elements should we print?
60 static int Vstruct_levels_to_print = 2;
61 
62 // TRUE means print struct array contents, up to the number of levels
63 // specified by struct_levels_to_print.
64 static bool Vprint_struct_array_contents = false;
65 
68 {
69  octave_base_value *retval = nullptr;
70 
71  if (numel () == 1)
73 
74  return retval;
75 }
76 
77 Cell
78 octave_struct::dotref (const octave_value_list& idx, bool auto_add)
79 {
80  Cell retval;
81 
82  assert (idx.length () == 1);
83 
84  std::string nm = idx(0).string_value ();
85 
87 
88  if (p != map.end ())
89  retval = map.contents (p);
90  else if (auto_add)
91  retval = (isempty ()) ? Cell (dim_vector (1, 1)) : Cell (dims ());
92  else
93  error_with_id ("Octave:invalid-indexing",
94  "structure has no member '%s'", nm.c_str ());
95 
96  return retval;
97 }
98 
99 static void
101 {
102  error ("invalid index for structure array assignment");
103 }
104 
105 static void
106 err_invalid_index_type (const std::string& nm, char t)
107 {
108  error ("%s cannot be indexed with %c", nm.c_str (), t);
109 }
110 
111 static void
112 maybe_warn_invalid_field_name (const std::string& key, const char *who)
113 {
114  if (! octave::valid_identifier (key))
115  {
116  if (who)
117  warning_with_id ("Octave:language-extension",
118  "%s: invalid structure field name '%s'",
119  who, key.c_str ());
120  else
121  warning_with_id ("Octave:language-extension",
122  "invalid structure field name '%s'",
123  key.c_str ());
124  }
125 }
126 
128 octave_struct::subsref (const std::string& type,
129  const std::list<octave_value_list>& idx,
130  int nargout)
131 {
133 
134  int skip = 1;
135 
136  switch (type[0])
137  {
138  case '(':
139  {
140  if (type.length () > 1 && type[1] == '.')
141  {
142  auto p = idx.begin ();
143  octave_value_list key_idx = *++p;
144 
145  const Cell tmp = dotref (key_idx);
146 
147  const Cell t = tmp.index (idx.front ());
148 
149  // Avoid creating a comma-separated list if the result is a
150  // single element.
151 
152  retval(0) = (t.numel () == 1) ? t(0) : octave_value (t, true);
153 
154  // We handled two index elements, so tell
155  // next_subsref to skip both of them.
156 
157  skip++;
158  }
159  else
160  retval(0) = do_index_op (idx.front ());
161  }
162  break;
163 
164  case '.':
165  {
166  const Cell t = dotref (idx.front ());
167 
168  // Avoid creating a comma-separated list if the result is a
169  // single element.
170 
171  retval(0) = (t.numel () == 1) ? t(0) : octave_value (t, true);
172  }
173  break;
174 
175  case '{':
177  break;
178 
179  default:
180  panic_impossible ();
181  }
182 
183  // FIXME: perhaps there should be an
184  // octave_value_list::next_subsref member function? See also
185  // octave_user_function::subsref.
186 
187  if (idx.size () > 1)
188  retval = retval(0).next_subsref (nargout, type, idx, skip);
189 
190  return retval;
191 }
192 
194 octave_struct::subsref (const std::string& type,
195  const std::list<octave_value_list>& idx,
196  bool auto_add)
197 {
199 
200  int skip = 1;
201 
202  switch (type[0])
203  {
204  case '(':
205  {
206  if (type.length () > 1 && type[1] == '.')
207  {
208  auto p = idx.begin ();
209  octave_value_list key_idx = *++p;
210 
211  const Cell tmp = dotref (key_idx, auto_add);
212 
213  const Cell t = tmp.index (idx.front (), auto_add);
214 
215  // Avoid creating a comma-separated list if the result is a
216  // single element.
217 
218  retval = (t.numel () == 1) ? t(0) : octave_value (t, true);
219 
220  // We handled two index elements, so tell
221  // next_subsref to skip both of them.
222 
223  skip++;
224  }
225  else
226  retval = do_index_op (idx.front (), auto_add);
227  }
228  break;
229 
230  case '.':
231  {
232  if (map.numel () > 0)
233  {
234  const Cell t = dotref (idx.front (), auto_add);
235 
236  // Avoid creating a comma-separated list if the result is a
237  // single element.
238 
239  retval = (t.numel () == 1) ? t(0) : octave_value (t, true);
240  }
241  }
242  break;
243 
244  case '{':
246  break;
247 
248  default:
249  panic_impossible ();
250  }
251 
252  // FIXME: perhaps there should be an
253  // octave_value_list::next_subsref member function? See also
254  // octave_user_function::subsref.
255 
256  if (idx.size () > 1)
257  retval = retval.next_subsref (auto_add, type, idx, skip);
258 
259  return retval;
260 }
261 
262 /*
263 %!test
264 %! x(1).a.a = 1;
265 %! x(2).a.a = 2;
266 %! assert (size (x), [1, 2]);
267 %! assert (x(1).a.a, 1);
268 %! assert (x(2).a.a, 2);
269 */
270 
273  const std::string& type)
274 {
276 
277  if (type.length () > 0 && type[0] == '.' && ! val.isstruct ())
278  retval = octave_map ();
279  else
280  retval = val;
281 
282  return retval;
283 }
284 
286 octave_struct::subsasgn (const std::string& type,
287  const std::list<octave_value_list>& idx,
288  const octave_value& rhs)
289 {
291 
292  int n = type.length ();
293 
294  octave_value t_rhs = rhs;
295 
296  if (idx.front ().empty ())
297  error ("missing index in indexed assignment");
298 
299  if (n > 1 && ! (type.length () == 2 && type[0] == '(' && type[1] == '.'))
300  {
301  switch (type[0])
302  {
303  case '(':
304  {
305  if (type.length () > 1 && type[1] == '.')
306  {
307  auto p = idx.begin ();
308  octave_value_list t_idx = *p;
309 
310  octave_value_list key_idx = *++p;
311 
312  assert (key_idx.length () == 1);
313 
314  std::string key = key_idx(0).string_value ();
315 
316  maybe_warn_invalid_field_name (key, "subsasgn");
317 
318  std::list<octave_value_list> next_idx (idx);
319 
320  // We handled two index elements, so subsasgn to
321  // needs to skip both of them.
322 
323  next_idx.erase (next_idx.begin ());
324  next_idx.erase (next_idx.begin ());
325 
326  std::string next_type = type.substr (2);
327 
328  Cell tmpc (1, 1);
329  auto pkey = map.seek (key);
330  if (pkey != map.end ())
331  {
332  map.contents (pkey).make_unique ();
333  tmpc = map.contents (pkey).index (idx.front (), true);
334  }
335 
336  // FIXME: better code reuse?
337  // cf. octave_cell::subsasgn and the case below.
338  if (tmpc.numel () != 1)
340 
341  octave_value& tmp = tmpc(0);
342 
343  bool orig_undefined = tmp.is_undefined ();
344 
345  if (orig_undefined || tmp.is_zero_by_zero ())
346  {
347  tmp = octave_value::empty_conv (next_type, rhs);
348  tmp.make_unique (); // probably a no-op.
349  }
350  else
351  // optimization: ignore the copy
352  // still stored inside our map.
353  tmp.make_unique (1);
354 
355  t_rhs =(orig_undefined
356  ? tmp.undef_subsasgn (next_type, next_idx, rhs)
357  : tmp.subsasgn (next_type, next_idx, rhs));
358  }
359  else
361  }
362  break;
363 
364  case '.':
365  {
366  octave_value_list key_idx = idx.front ();
367 
368  assert (key_idx.length () == 1);
369 
370  std::string key = key_idx(0).string_value ();
371 
372  maybe_warn_invalid_field_name (key, "subsasgn");
373 
374  std::list<octave_value_list> next_idx (idx);
375 
376  next_idx.erase (next_idx.begin ());
377 
378  std::string next_type = type.substr (1);
379 
380  Cell tmpc (1, 1);
381  auto pkey = map.seek (key);
382  if (pkey != map.end ())
383  {
384  map.contents (pkey).make_unique ();
385  tmpc = map.contents (pkey);
386  }
387 
388  // FIXME: better code reuse?
389 
390  if (tmpc.numel () == 1)
391  {
392  octave_value& tmp = tmpc(0);
393 
394  bool orig_undefined = tmp.is_undefined ();
395 
396  if (orig_undefined || tmp.is_zero_by_zero ())
397  {
398  tmp = octave_value::empty_conv (next_type, rhs);
399  tmp.make_unique (); // probably a no-op.
400  }
401  else
402  // optimization: ignore the copy
403  // still stored inside our map.
404  tmp.make_unique (1);
405 
406  t_rhs = (orig_undefined
407  ? tmp.undef_subsasgn (next_type, next_idx, rhs)
408  : tmp.subsasgn (next_type, next_idx, rhs));
409  }
410  else
412  }
413  break;
414 
415  case '{':
417  break;
418 
419  default:
420  panic_impossible ();
421  }
422  }
423 
424  switch (type[0])
425  {
426  case '(':
427  {
428  if (n > 1 && type[1] == '.')
429  {
430  auto p = idx.begin ();
431  octave_value_list key_idx = *++p;
432  octave_value_list idxf = idx.front ();
433 
434  assert (key_idx.length () == 1);
435 
436  std::string key = key_idx(0).string_value ();
437 
438  maybe_warn_invalid_field_name (key, "subsasgn");
439 
440  if (t_rhs.is_cs_list ())
441  {
442  Cell tmp_cell = Cell (t_rhs.list_value ());
443 
444  // Inquire the proper shape of the RHS.
445 
446  dim_vector didx = dims ().redim (idxf.length ());
447  for (octave_idx_type k = 0; k < idxf.length (); k++)
448  if (! idxf(k).is_magic_colon ())
449  didx(k) = idxf(k).numel ();
450 
451  if (didx.numel () == tmp_cell.numel ())
452  tmp_cell = tmp_cell.reshape (didx);
453 
454  map.assign (idxf, key, tmp_cell);
455 
456  count++;
457  retval = octave_value (this);
458  }
459  else
460  {
461  const octave_map& cmap = const_cast<const octave_map &> (map);
462  // cast to const reference, avoid forced key insertion.
463  if (idxf.all_scalars ()
464  || cmap.contents (key).index (idxf, true).numel () == 1)
465  {
466  map.assign (idxf,
467  key, Cell (t_rhs.storable_value ()));
468 
469  count++;
470  retval = octave_value (this);
471  }
472  else
474  }
475  }
476  else
477  {
478  if (t_rhs.isstruct () || t_rhs.isobject ())
479  {
480  octave_map rhs_map = t_rhs.xmap_value ("invalid structure assignment");
481 
482  map.assign (idx.front (), rhs_map);
483 
484  count++;
485  retval = octave_value (this);
486  }
487  else
488  {
489  if (! t_rhs.isnull ())
490  error ("invalid structure assignment");
491 
492  map.delete_elements (idx.front ());
493 
494  count++;
495  retval = octave_value (this);
496  }
497  }
498  }
499  break;
500 
501  case '.':
502  {
503  octave_value_list key_idx = idx.front ();
504 
505  assert (key_idx.length () == 1);
506 
507  std::string key = key_idx(0).string_value ();
508 
509  maybe_warn_invalid_field_name (key, "subsasgn");
510 
511  if (t_rhs.is_cs_list ())
512  {
513  Cell tmp_cell = Cell (t_rhs.list_value ());
514 
515  // The shape of the RHS is irrelevant, we just want
516  // the number of elements to agree and to preserve the
517  // shape of the left hand side of the assignment.
518 
519  if (numel () == tmp_cell.numel ())
520  tmp_cell = tmp_cell.reshape (dims ());
521 
522  map.setfield (key, tmp_cell);
523  }
524  else
525  {
526  Cell tmp_cell(1, 1);
527  tmp_cell(0) = t_rhs.storable_value ();
528  map.setfield (key, tmp_cell);
529  }
530 
531  count++;
532  retval = octave_value (this);
533  }
534  break;
535 
536  case '{':
538  break;
539 
540  default:
541  panic_impossible ();
542  }
543 
544  retval.maybe_mutate ();
545 
546  return retval;
547 }
548 
550 octave_struct::do_index_op (const octave_value_list& idx, bool resize_ok)
551 {
552  if (idx.length () == 0)
553  {
555  return map;
556  }
557  else // octave_map handles indexing itself.
558  return map.index (idx, resize_ok);
559 }
560 
561 size_t
563 {
564  // Neglect the size of the fieldnames.
565 
566  size_t retval = 0;
567 
568  for (auto p = map.cbegin (); p != map.cend (); p++)
569  {
570  std::string key = map.key (p);
571 
573 
574  retval += val.byte_size ();
575  }
576 
577  return retval;
578 }
579 
580 void
581 octave_struct::print (std::ostream& os, bool)
582 {
583  print_raw (os);
584 }
585 
586 void
587 octave_struct::print_raw (std::ostream& os, bool) const
588 {
590 
592 
593  if (Vstruct_levels_to_print >= 0)
594  {
595  bool max_depth_reached = Vstruct_levels_to_print-- == 0;
596 
597  bool print_fieldnames_only
598  = (max_depth_reached || ! Vprint_struct_array_contents);
599 
601 
602  indent (os);
603  dim_vector dv = dims ();
604  os << dv.str () << " struct array containing the fields:";
605  newline (os);
606 
608 
609  string_vector key_list = map.fieldnames ();
610 
611  for (octave_idx_type i = 0; i < key_list.numel (); i++)
612  {
613  std::string key = key_list[i];
614 
615  Cell val = map.contents (key);
616 
617  if (i > 0 || ! Vcompact_format)
618  newline (os);
619 
620  if (print_fieldnames_only)
621  {
622  indent (os);
623  os << key;
624  }
625  else
626  {
627  octave_value tmp (val);
628  tmp.print_with_name (os, key);
629  }
630  }
631 
632  if (print_fieldnames_only)
633  newline (os);
634 
637  }
638  else
639  {
640  indent (os);
641  os << "<structure>";
642  newline (os);
643  }
644 }
645 
646 bool
647 octave_struct::print_name_tag (std::ostream& os, const std::string& name) const
648 {
649  bool retval = false;
650 
651  indent (os);
652 
653  if (Vstruct_levels_to_print < 0)
654  os << name << " = ";
655  else
656  {
657  os << name << " =";
658  newline (os);
659  if (! Vcompact_format)
660  newline (os);
661 
662  retval = true;
663  }
664 
665  return retval;
666 }
667 
668 static bool
669 scalar (const dim_vector& dims)
670 {
671  return dims.ndims () == 2 && dims(0) == 1 && dims(1) == 1;
672 }
673 
674 std::string
677 {
678  octave_value val;
679  if (map.rows () == 1 || map.columns () == 1)
680  {
681  // Vector struct. Columns are fields, rows are values.
682 
683  Cell cval = map.contents (c);
684 
685  val = cval(r);
686  }
687  else
688  {
689  // 2-d struct array. Rows and columns index individual
690  // scalar structs.
691 
692  val = map(r,c);
693  }
694 
695  std::string tname = val.type_name ();
696  dim_vector dv = val.dims ();
697  std::string dimstr = dv.str ();
698  return "[" + dimstr + " " + tname + "]";
699 }
700 
701 
702 bool
703 octave_struct::save_ascii (std::ostream& os)
704 {
705  octave_map m = map_value ();
706 
707  octave_idx_type nf = m.nfields ();
708 
709  const dim_vector dv = dims ();
710 
711  os << "# ndims: " << dv.ndims () << "\n";
712 
713  for (int i = 0; i < dv.ndims (); i++)
714  os << ' ' << dv(i);
715  os << "\n";
716 
717  os << "# length: " << nf << "\n";
718 
719  // Iterating over the list of keys will preserve the order of the
720  // fields.
721  string_vector keys = m.fieldnames ();
722 
723  for (octave_idx_type i = 0; i < nf; i++)
724  {
725  std::string key = keys(i);
726 
727  octave_value val = map.contents (key);
728 
729  bool b = save_text_data (os, val, key, false, 0);
730 
731  if (! b)
732  return ! os.fail ();
733  }
734 
735  return true;
736 }
737 
738 bool
739 octave_struct::load_ascii (std::istream& is)
740 {
741  octave_idx_type len = 0;
742  dim_vector dv (1, 1);
743  bool success = true;
744 
745  // KLUGE: earlier Octave versions did not save extra dimensions with struct,
746  // and as a result did not preserve dimensions for empty structs.
747  // The default dimensions were 1x1, which we want to preserve.
748  string_vector keywords(2);
749 
750  keywords[0] = "ndims";
751  keywords[1] = "length";
752 
753  std::string kw;
754 
755  if (extract_keyword (is, keywords, kw, len, true))
756  {
757  if (kw == keywords[0])
758  {
759  int mdims = std::max (static_cast<int> (len), 2);
760  dv.resize (mdims);
761  for (int i = 0; i < mdims; i++)
762  is >> dv(i);
763 
764  success = extract_keyword (is, keywords[1], len);
765  }
766  }
767  else
768  success = false;
769 
770  if (! success || len < 0)
771  error ("load: failed to extract number of elements in structure");
772 
773  if (len > 0)
774  {
775  octave_map m (dv);
776 
777  for (octave_idx_type j = 0; j < len; j++)
778  {
779  octave_value t2;
780  bool dummy;
781 
782  // recurse to read cell elements
783  std::string nm = read_text_data (is, "", dummy, t2, j, false);
784 
785  if (! is)
786  break;
787 
788  Cell tcell = (t2.iscell () ? t2.xcell_value ("load: internal error loading struct elements") : Cell (t2));
789 
790  m.setfield (nm, tcell);
791  }
792 
793  if (! is)
794  error ("load: failed to load structure");
795 
796  map = m;
797  }
798  else if (len == 0)
799  map = octave_map (dv);
800  else
801  panic_impossible ();
802 
803  return success;
804 }
805 
806 bool
807 octave_struct::save_binary (std::ostream& os, bool save_as_floats)
808 {
809  octave_map m = map_value ();
810 
811  octave_idx_type nf = m.nfields ();
812 
813  dim_vector dv = dims ();
814  if (dv.ndims () < 1)
815  return false;
816 
817  // Use negative value for ndims
818  int32_t di = - dv.ndims ();
819  os.write (reinterpret_cast<char *> (&di), 4);
820  for (int i = 0; i < dv.ndims (); i++)
821  {
822  di = dv(i);
823  os.write (reinterpret_cast<char *> (&di), 4);
824  }
825 
826  int32_t len = nf;
827  os.write (reinterpret_cast<char *> (&len), 4);
828 
829  // Iterating over the list of keys will preserve the order of the
830  // fields.
831  string_vector keys = m.fieldnames ();
832 
833  for (octave_idx_type i = 0; i < nf; i++)
834  {
835  std::string key = keys(i);
836 
837  octave_value val = map.contents (key);
838 
839  bool b = save_binary_data (os, val, key, "", 0, save_as_floats);
840 
841  if (! b)
842  return ! os.fail ();
843  }
844 
845  return true;
846 }
847 
848 bool
849 octave_struct::load_binary (std::istream& is, bool swap,
851 {
852  bool success = true;
853  int32_t len;
854  if (! is.read (reinterpret_cast<char *> (&len), 4))
855  return false;
856  if (swap)
857  swap_bytes<4> (&len);
858 
859  dim_vector dv (1, 1);
860 
861  if (len < 0)
862  {
863  // We have explicit dimensions.
864  int mdims = -len;
865 
866  int32_t di;
867  dv.resize (mdims);
868 
869  for (int i = 0; i < mdims; i++)
870  {
871  if (! is.read (reinterpret_cast<char *> (&di), 4))
872  return false;
873  if (swap)
874  swap_bytes<4> (&di);
875  dv(i) = di;
876  }
877 
878  if (! is.read (reinterpret_cast<char *> (&len), 4))
879  return false;
880  if (swap)
881  swap_bytes<4> (&len);
882  }
883 
884  if (len > 0)
885  {
886  octave_map m (dv);
887 
888  for (octave_idx_type j = 0; j < len; j++)
889  {
890  octave_value t2;
891  bool dummy;
892  std::string doc;
893 
894  // recurse to read cell elements
895  std::string nm = read_binary_data (is, swap, fmt, "",
896  dummy, t2, doc);
897 
898  if (! is)
899  break;
900 
901  Cell tcell = (t2.iscell () ? t2.xcell_value ("load: internal error loading struct elements") : Cell (t2));
902 
903  m.setfield (nm, tcell);
904  }
905 
906  if (! is)
907  error ("load: failed to load structure");
908 
909  map = m;
910  }
911  else if (len == 0)
912  map = octave_map (dv);
913  else
914  success = false;
915 
916  return success;
917 }
918 
919 bool
921  bool save_as_floats)
922 {
923 #if defined (HAVE_HDF5)
924 
925  hid_t data_hid = -1;
926 
927 #if defined (HAVE_HDF5_18)
928  data_hid = H5Gcreate (loc_id, name, octave_H5P_DEFAULT, octave_H5P_DEFAULT,
930 #else
931  data_hid = H5Gcreate (loc_id, name, 0);
932 #endif
933  if (data_hid < 0) return false;
934 
935  // recursively add each element of the structure to this group
936  octave_map m = map_value ();
937 
938  octave_idx_type nf = m.nfields ();
939 
940  // Iterating over the list of keys will preserve the order of the
941  // fields.
942  string_vector keys = m.fieldnames ();
943 
944  for (octave_idx_type i = 0; i < nf; i++)
945  {
946  std::string key = keys(i);
947 
948  octave_value val = map.contents (key);
949 
950  bool retval2 = add_hdf5_data (data_hid, val, key, "", false,
951  save_as_floats);
952 
953  if (! retval2)
954  break;
955  }
956 
957  H5Gclose (data_hid);
958 
959  return true;
960 
961 #else
962  octave_unused_parameter (loc_id);
963  octave_unused_parameter (name);
964  octave_unused_parameter (save_as_floats);
965 
966  warn_save ("hdf5");
967 
968  return false;
969 #endif
970 }
971 
972 bool
974 {
975  bool retval = false;
976 
977 #if defined (HAVE_HDF5)
978 
979  hdf5_callback_data dsub;
980 
981  herr_t retval2 = 0;
982  octave_map m (dim_vector (1, 1));
983  int current_item = 0;
984  hsize_t num_obj = 0;
985 #if defined (HAVE_HDF5_18)
986  hid_t group_id = H5Gopen (loc_id, name, octave_H5P_DEFAULT);
987 #else
988  hid_t group_id = H5Gopen (loc_id, name);
989 #endif
990  H5Gget_num_objs (group_id, &num_obj);
991  H5Gclose (group_id);
992 
993  // FIXME: fields appear to be sorted alphabetically on loading.
994  // Why is that happening?
995 
996  while (current_item < static_cast<int> (num_obj)
997  && (retval2 = hdf5_h5g_iterate (loc_id, name, &current_item,
998  &dsub)) > 0)
999  {
1000  octave_value t2 = dsub.tc;
1001 
1002  Cell tcell = (t2.iscell () ? t2.xcell_value ("load: internal error loading struct elements") : Cell (t2));
1003 
1004  m.setfield (dsub.name, tcell);
1005 
1006  }
1007 
1008  if (retval2 >= 0)
1009  {
1010  map = m;
1011  retval = true;
1012  }
1013 
1014 #else
1015  octave_unused_parameter (loc_id);
1016  octave_unused_parameter (name);
1017 
1018  warn_load ("hdf5");
1019 #endif
1020 
1021  return retval;
1022 }
1023 
1024 mxArray *
1026 {
1027  int nf = nfields ();
1028  string_vector kv = map_keys ();
1029 
1030  OCTAVE_LOCAL_BUFFER (const char *, f, nf);
1031 
1032  for (int i = 0; i < nf; i++)
1033  f[i] = kv[i].c_str ();
1034 
1035  mxArray *retval = new mxArray (dims (), nf, f);
1036 
1037  mxArray **elts = static_cast<mxArray **> (retval->get_data ());
1038 
1039  mwSize nel = numel ();
1040 
1041  mwSize ntot = nf * nel;
1042 
1043  for (int i = 0; i < nf; i++)
1044  {
1045  Cell c = map.contents (kv[i]);
1046 
1047  const octave_value *p = c.data ();
1048 
1049  mwIndex k = 0;
1050  for (mwIndex j = i; j < ntot; j += nf)
1051  elts[j] = new mxArray (p[k++]);
1052  }
1053 
1054  return retval;
1055 }
1056 
1059 {
1060  if (n < map.numel ())
1061  return map.checkelem (n);
1062  else
1063  return octave_value ();
1064 }
1065 
1066 bool
1068  const octave_value& x)
1069 {
1070  bool retval = false;
1071 
1072  if (n < map.numel ())
1073  {
1074  // To avoid copying the scalar struct, it just stores a pointer to
1075  // itself.
1076  const octave_scalar_map *sm_ptr;
1077  void *here = reinterpret_cast<void *>(&sm_ptr);
1078  return (x.get_rep ().fast_elem_insert_self (here, btyp_struct)
1079  && map.fast_elem_insert (n, *sm_ptr));
1080  }
1081 
1082  return retval;
1083 }
1084 
1086  "struct");
1087 
1090 {
1092 
1093  assert (idx.length () == 1);
1094 
1095  std::string nm = idx(0).string_value ();
1096 
1097  maybe_warn_invalid_field_name (nm, "subsref");
1098 
1099  retval = map.getfield (nm);
1100 
1101  if (! auto_add && retval.is_undefined ())
1102  error_with_id ("Octave:invalid-indexing",
1103  "structure has no member '%s'", nm.c_str ());
1104 
1105  return retval;
1106 }
1107 
1110  const std::list<octave_value_list>& idx)
1111 {
1113 
1114  if (type[0] == '.')
1115  {
1116  int skip = 1;
1117 
1118  retval = dotref (idx.front ());
1119 
1120  if (idx.size () > 1)
1121  retval = retval.next_subsref (type, idx, skip);
1122  }
1123  else
1124  retval = to_array ().subsref (type, idx);
1125 
1126  return retval;
1127 }
1128 
1131  const std::list<octave_value_list>& idx,
1132  int nargout)
1133 {
1135 
1136  if (type[0] == '.')
1137  {
1138  int skip = 1;
1139 
1140  retval(0) = dotref (idx.front ());
1141 
1142  if (idx.size () > 1)
1143  retval = retval(0).next_subsref (nargout, type, idx, skip);
1144  }
1145  else
1146  retval = to_array ().subsref (type, idx, nargout);
1147 
1148  return retval;
1149 }
1150 
1153  const std::list<octave_value_list>& idx,
1154  bool auto_add)
1155 {
1157 
1158  if (type[0] == '.')
1159  {
1160  int skip = 1;
1161 
1162  retval = dotref (idx.front (), auto_add);
1163 
1164  if (idx.size () > 1)
1165  retval = retval.next_subsref (auto_add, type, idx, skip);
1166  }
1167  else
1168  retval = to_array ().subsref (type, idx, auto_add);
1169 
1170  return retval;
1171 }
1172 
1173 /*
1174 %!test
1175 %! x(1).a.a = 1;
1176 %! x(2).a.a = 2;
1177 %! assert (size (x), [1, 2]);
1178 %! assert (x(1).a.a, 1);
1179 %! assert (x(2).a.a, 2);
1180 */
1181 
1184  const std::string& type)
1185 {
1187 
1188  if (type.length () > 0 && type[0] == '.' && ! val.isstruct ())
1189  retval = octave_map ();
1190  else
1191  retval = val;
1192 
1193  return retval;
1194 }
1195 
1198  const std::list<octave_value_list>& idx,
1199  const octave_value& rhs)
1200 {
1202 
1203  if (idx.front ().empty ())
1204  error ("missing index in indexed assignment");
1205 
1206  if (type[0] == '.')
1207  {
1208  int n = type.length ();
1209 
1210  octave_value t_rhs = rhs;
1211 
1212  octave_value_list key_idx = idx.front ();
1213 
1214  assert (key_idx.length () == 1);
1215 
1216  std::string key = key_idx(0).string_value ();
1217 
1218  maybe_warn_invalid_field_name (key, "subsasgn");
1219 
1220  if (n > 1)
1221  {
1222  std::list<octave_value_list> next_idx (idx);
1223 
1224  next_idx.erase (next_idx.begin ());
1225 
1226  std::string next_type = type.substr (1);
1227 
1228  octave_value tmp;
1229  auto pkey = map.seek (key);
1230  if (pkey != map.end ())
1231  {
1232  map.contents (pkey).make_unique ();
1233  tmp = map.contents (pkey);
1234  }
1235 
1236  bool orig_undefined = tmp.is_undefined ();
1237 
1238  if (orig_undefined || tmp.is_zero_by_zero ())
1239  {
1240  tmp = octave_value::empty_conv (next_type, rhs);
1241  tmp.make_unique (); // probably a no-op.
1242  }
1243  else
1244  // optimization: ignore the copy still stored inside our map.
1245  tmp.make_unique (1);
1246 
1247  t_rhs = (orig_undefined
1248  ? tmp.undef_subsasgn (next_type, next_idx, rhs)
1249  : tmp.subsasgn (next_type, next_idx, rhs));
1250  }
1251 
1252  map.setfield (key, t_rhs.storable_value ());
1253 
1254  count++;
1255  retval = this;
1256  }
1257  else
1258  {
1259  // Forward this case to octave_struct.
1260  octave_value tmp (new octave_struct (octave_map (map)));
1261  retval = tmp.subsasgn (type, idx, rhs);
1262  }
1263 
1264  return retval;
1265 }
1266 
1269 {
1270  // octave_map handles indexing itself.
1271  return octave_map (map).index (idx, resize_ok);
1272 }
1273 
1274 size_t
1276 {
1277  // Neglect the size of the fieldnames.
1278 
1279  size_t retval = 0;
1280 
1281  for (auto p = map.cbegin (); p != map.cend (); p++)
1282  {
1283  std::string key = map.key (p);
1284 
1285  octave_value val = octave_value (map.contents (p));
1286 
1287  retval += val.byte_size ();
1288  }
1289 
1290  return retval;
1291 }
1292 
1293 void
1294 octave_scalar_struct::print (std::ostream& os, bool)
1295 {
1296  print_raw (os);
1297 }
1298 
1299 void
1300 octave_scalar_struct::print_raw (std::ostream& os, bool) const
1301 {
1302  octave::unwind_protect frame;
1303 
1305 
1306  if (Vstruct_levels_to_print >= 0)
1307  {
1308  bool max_depth_reached = Vstruct_levels_to_print-- == 0;
1309 
1310  bool print_fieldnames_only = max_depth_reached;
1311 
1313 
1314  indent (os);
1315  os << "scalar structure containing the fields:";
1316  newline (os);
1317  if (! Vcompact_format)
1318  newline (os);
1319 
1321 
1322  string_vector key_list = map.fieldnames ();
1323 
1324  for (octave_idx_type i = 0; i < key_list.numel (); i++)
1325  {
1326  std::string key = key_list[i];
1327 
1328  octave_value val = map.contents (key);
1329 
1330  if (print_fieldnames_only)
1331  {
1332  indent (os);
1333  os << key;
1334  dim_vector dv = val.dims ();
1335  os << ": " << dv.str () << ' ' << val.type_name ();
1336  newline (os);
1337  }
1338  else
1339  val.print_with_name (os, key);
1340  }
1341 
1344  }
1345  else
1346  {
1347  indent (os);
1348  os << "<structure>";
1349  newline (os);
1350  }
1351 }
1352 
1353 bool
1355  const std::string& name) const
1356 {
1357  bool retval = false;
1358 
1359  indent (os);
1360 
1361  if (Vstruct_levels_to_print < 0)
1362  os << name << " = ";
1363  else
1364  {
1365  os << name << " =";
1366  newline (os);
1367  if (! Vcompact_format)
1368  newline (os);
1369 
1370  retval = true;
1371  }
1372 
1373  return retval;
1374 }
1375 
1376 std::string
1379 {
1380  // Scalar struct. Rows are fields, single column for values.
1381 
1382  octave_value val = map.contents (r);
1383 
1384  std::string tname = val.type_name ();
1385  dim_vector dv = val.dims ();
1386  std::string dimstr = dv.str ();
1387  return "[" + dimstr + " " + tname + "]";
1388 }
1389 
1390 bool
1392 {
1393  octave_map m = map_value ();
1394 
1395  octave_idx_type nf = m.nfields ();
1396 
1397  const dim_vector dv = dims ();
1398 
1399  os << "# ndims: " << dv.ndims () << "\n";
1400 
1401  for (int i = 0; i < dv.ndims (); i++)
1402  os << ' ' << dv(i);
1403  os << "\n";
1404 
1405  os << "# length: " << nf << "\n";
1406 
1407  // Iterating over the list of keys will preserve the order of the
1408  // fields.
1409  string_vector keys = m.fieldnames ();
1410 
1411  for (octave_idx_type i = 0; i < nf; i++)
1412  {
1413  std::string key = keys(i);
1414 
1415  octave_value val = map.contents (key);
1416 
1417  bool b = save_text_data (os, val, key, false, 0);
1418 
1419  if (! b)
1420  return ! os.fail ();
1421  }
1422 
1423  return true;
1424 }
1425 
1426 bool
1428 {
1429  octave_idx_type len = 0;
1430 
1431  if (! extract_keyword (is, "length", len) || len < 0)
1432  error ("load: failed to extract number of elements in structure");
1433 
1434  if (len > 0)
1435  {
1437 
1438  for (octave_idx_type j = 0; j < len; j++)
1439  {
1440  octave_value t2;
1441  bool dummy;
1442 
1443  // recurse to read cell elements
1444  std::string nm
1445  = read_text_data (is, "", dummy, t2, j, false);
1446 
1447  if (! is)
1448  break;
1449 
1450  m.setfield (nm, t2);
1451  }
1452 
1453  if (! is)
1454  error ("load: failed to load structure");
1455 
1456  map = m;
1457  }
1458  else if (len == 0)
1459  map = octave_scalar_map ();
1460  else
1461  panic_impossible ();
1462 
1463  return true;
1464 }
1465 
1466 bool
1467 octave_scalar_struct::save_binary (std::ostream& os, bool save_as_floats)
1468 {
1469  octave_map m = map_value ();
1470 
1471  octave_idx_type nf = m.nfields ();
1472 
1473  int32_t len = nf;
1474  os.write (reinterpret_cast<char *> (&len), 4);
1475 
1476  // Iterating over the list of keys will preserve the order of the
1477  // fields.
1478  string_vector keys = m.fieldnames ();
1479 
1480  for (octave_idx_type i = 0; i < nf; i++)
1481  {
1482  std::string key = keys(i);
1483 
1484  octave_value val = map.contents (key);
1485 
1486  bool b = save_binary_data (os, val, key, "", 0, save_as_floats);
1487 
1488  if (! b)
1489  return ! os.fail ();
1490  }
1491 
1492  return true;
1493 }
1494 
1495 bool
1496 octave_scalar_struct::load_binary (std::istream& is, bool swap,
1498 {
1499  bool success = true;
1500  int32_t len;
1501  if (! is.read (reinterpret_cast<char *> (&len), 4))
1502  return false;
1503  if (swap)
1504  swap_bytes<4> (&len);
1505 
1506  if (len > 0)
1507  {
1509 
1510  for (octave_idx_type j = 0; j < len; j++)
1511  {
1512  octave_value t2;
1513  bool dummy;
1514  std::string doc;
1515 
1516  // recurse to read cell elements
1517  std::string nm = read_binary_data (is, swap, fmt, "",
1518  dummy, t2, doc);
1519 
1520  if (! is)
1521  break;
1522 
1523  m.setfield (nm, t2);
1524  }
1525 
1526  if (! is)
1527  error ("load: failed to load structure");
1528 
1529  map = m;
1530  }
1531  else if (len == 0)
1532  map = octave_scalar_map ();
1533  else
1534  success = false;
1535 
1536  return success;
1537 }
1538 
1539 bool
1541  bool save_as_floats)
1542 {
1543 #if defined (HAVE_HDF5)
1544 
1545  hid_t data_hid = -1;
1546 
1547 #if defined (HAVE_HDF5_18)
1548  data_hid = H5Gcreate (loc_id, name, octave_H5P_DEFAULT, octave_H5P_DEFAULT,
1550 #else
1551  data_hid = H5Gcreate (loc_id, name, 0);
1552 #endif
1553  if (data_hid < 0) return false;
1554 
1555  // recursively add each element of the structure to this group
1557 
1558  octave_idx_type nf = m.nfields ();
1559 
1560  // Iterating over the list of keys will preserve the order of the
1561  // fields.
1562  string_vector keys = m.fieldnames ();
1563 
1564  for (octave_idx_type i = 0; i < nf; i++)
1565  {
1566  std::string key = keys(i);
1567 
1568  octave_value val = map.contents (key);
1569 
1570  bool retval2 = add_hdf5_data (data_hid, val, key, "", false,
1571  save_as_floats);
1572 
1573  if (! retval2)
1574  break;
1575  }
1576 
1577  H5Gclose (data_hid);
1578 
1579  return true;
1580 
1581 #else
1582  octave_unused_parameter (loc_id);
1583  octave_unused_parameter (name);
1584  octave_unused_parameter (save_as_floats);
1585 
1586  warn_save ("hdf5");
1587 
1588  return false;
1589 #endif
1590 }
1591 
1592 bool
1594 {
1595  bool retval = false;
1596 
1597 #if defined (HAVE_HDF5)
1598 
1599  hdf5_callback_data dsub;
1600 
1601  herr_t retval2 = 0;
1603  int current_item = 0;
1604  hsize_t num_obj = 0;
1605 #if defined (HAVE_HDF5_18)
1606  hid_t group_id = H5Gopen (loc_id, name, octave_H5P_DEFAULT);
1607 #else
1608  hid_t group_id = H5Gopen (loc_id, name);
1609 #endif
1610  H5Gget_num_objs (group_id, &num_obj);
1611  H5Gclose (group_id);
1612 
1613  // FIXME: fields appear to be sorted alphabetically on loading.
1614  // Why is that happening?
1615 
1616  while (current_item < static_cast<int> (num_obj)
1617  && (retval2 = hdf5_h5g_iterate (loc_id, name, &current_item,
1618  &dsub)) > 0)
1619  {
1620  octave_value t2 = dsub.tc;
1621 
1622  m.setfield (dsub.name, t2);
1623 
1624  }
1625 
1626  if (retval2 >= 0)
1627  {
1628  map = m;
1629  retval = true;
1630  }
1631 
1632 #else
1633  octave_unused_parameter (loc_id);
1634  octave_unused_parameter (name);
1635 
1636  warn_load ("hdf5");
1637 #endif
1638 
1639  return retval;
1640 }
1641 
1642 mxArray *
1644 {
1645  int nf = nfields ();
1646  string_vector kv = map_keys ();
1647 
1648  OCTAVE_LOCAL_BUFFER (const char *, f, nf);
1649 
1650  for (int i = 0; i < nf; i++)
1651  f[i] = kv[i].c_str ();
1652 
1653  mxArray *retval = new mxArray (dims (), nf, f);
1654 
1655  mxArray **elts = static_cast<mxArray **> (retval->get_data ());
1656 
1657  mwSize nel = numel ();
1658 
1659  mwSize ntot = nf * nel;
1660 
1661  for (int i = 0; i < nf; i++)
1662  {
1663  Cell c = map.contents (kv[i]);
1664 
1665  const octave_value *p = c.data ();
1666 
1667  mwIndex k = 0;
1668  for (mwIndex j = i; j < ntot; j += nf)
1669  elts[j] = new mxArray (p[k++]);
1670  }
1671 
1672  return retval;
1673 }
1674 
1677 {
1678  return new octave_struct (octave_map (map));
1679 }
1680 
1681 bool
1683  builtin_type_t btyp) const
1684 {
1685 
1686  if (btyp == btyp_struct)
1687  {
1688  *(reinterpret_cast<const octave_scalar_map **>(where)) = &map;
1689  return true;
1690  }
1691  else
1692  return false;
1693 }
1694 
1695 DEFUN (struct, args, ,
1696  doc: /* -*- texinfo -*-
1697 @deftypefn {} {@var{s} =} struct ()
1698 @deftypefnx {} {@var{s} =} struct (@var{field1}, @var{value1}, @var{field2}, @var{value2}, @dots{})
1699 @deftypefnx {} {@var{s} =} struct (@var{obj})
1700 
1701 Create a scalar or array structure and initialize its values.
1702 
1703 The @var{field1}, @var{field2}, @dots{} variables are strings specifying the
1704 names of the fields and the @var{value1}, @var{value2}, @dots{} variables
1705 can be of any type.
1706 
1707 If the values are cell arrays, create a structure array and initialize its
1708 values. The dimensions of each cell array of values must match. Singleton
1709 cells and non-cell values are repeated so that they fill the entire array.
1710 If the cells are empty, create an empty structure array with the specified
1711 field names.
1712 
1713 If the argument is an object, return the underlying struct.
1714 
1715 Observe that the syntax is optimized for struct @strong{arrays}. Consider
1716 the following examples:
1717 
1718 @example
1719 @group
1720 struct ("foo", 1)
1721  @result{} scalar structure containing the fields:
1722  foo = 1
1723 
1724 struct ("foo", @{@})
1725  @result{} 0x0 struct array containing the fields:
1726  foo
1727 
1728 struct ("foo", @{ @{@} @})
1729  @result{} scalar structure containing the fields:
1730  foo = @{@}(0x0)
1731 
1732 struct ("foo", @{1, 2, 3@})
1733  @result{} 1x3 struct array containing the fields:
1734  foo
1735 
1736 @end group
1737 @end example
1738 
1739 @noindent
1740 The first case is an ordinary scalar struct---one field, one value. The
1741 second produces an empty struct array with one field and no values, since
1742 being passed an empty cell array of struct array values. When the value is
1743 a cell array containing a single entry, this becomes a scalar struct with
1744 that single entry as the value of the field. That single entry happens
1745 to be an empty cell array.
1746 
1747 Finally, if the value is a non-scalar cell array, then @code{struct}
1748 produces a struct @strong{array}.
1749 @seealso{cell2struct, fieldnames, getfield, setfield, rmfield, isfield, orderfields, isstruct, structfun}
1750 @end deftypefn */)
1751 {
1752  int nargin = args.length ();
1753 
1754  // struct ([]) returns an empty struct.
1755 
1756  // struct (empty_matrix) returns an empty struct with the same
1757  // dimensions as the empty matrix.
1758 
1759  // Note that struct () creates a 1x1 struct with no fields for
1760  // compatibility with Matlab.
1761 
1762  if (nargin == 1 && args(0).isstruct ())
1763  return ovl (args(0));
1764 
1765  if (nargin == 1 && args(0).isobject ())
1766  return ovl (args(0).map_value ());
1767 
1768  if ((nargin == 1 || nargin == 2)
1769  && args(0).isempty () && args(0).is_real_matrix ())
1770  {
1771  if (nargin == 2)
1772  {
1773  Array<std::string> cstr = args(1).xcellstr_value ("struct: second argument should be a cell array of field names");
1774 
1775  return ovl (octave_map (args(0).dims (), cstr));
1776  }
1777  else
1778  return ovl (octave_map (args(0).dims ()));
1779  }
1780 
1781  // Check for "field", VALUE pairs.
1782 
1783  for (int i = 0; i < nargin; i += 2)
1784  {
1785  if (! args(i).is_string () || i + 1 >= nargin)
1786  error (R"(struct: additional arguments must occur as "field", VALUE pairs)");
1787  }
1788 
1789  // Check that the dimensions of the values correspond.
1790 
1791  dim_vector dims (1, 1);
1792 
1793  int first_dimensioned_value = 0;
1794 
1795  for (int i = 1; i < nargin; i += 2)
1796  {
1797  if (args(i).iscell ())
1798  {
1799  dim_vector argdims (args(i).dims ());
1800 
1801  if (! scalar (argdims))
1802  {
1803  if (! first_dimensioned_value)
1804  {
1805  dims = argdims;
1806  first_dimensioned_value = i + 1;
1807  }
1808  else if (dims != argdims)
1809  {
1810  error ("struct: dimensions of parameter %d "
1811  "do not match those of parameter %d",
1812  first_dimensioned_value, i+1);
1813  }
1814  }
1815  }
1816  }
1817 
1818  // Create the return value.
1819 
1820  octave_map map (dims);
1821 
1822  for (int i = 0; i < nargin; i+= 2)
1823  {
1824  // Get key.
1825 
1826  std::string key (args(i).string_value ());
1827 
1828  maybe_warn_invalid_field_name (key, "struct");
1829 
1830  // Value may be v, { v }, or { v1, v2, ... }
1831  // In the first two cases, we need to create a cell array of
1832  // the appropriate dimensions filled with v. In the last case,
1833  // the cell array has already been determined to be of the
1834  // correct dimensions.
1835 
1836  if (args(i+1).iscell ())
1837  {
1838  const Cell c (args(i+1).cell_value ());
1839 
1840  if (scalar (c.dims ()))
1841  map.setfield (key, Cell (dims, c(0)));
1842  else
1843  map.setfield (key, c);
1844  }
1845  else
1846  map.setfield (key, Cell (dims, args(i+1)));
1847  }
1848 
1849  return ovl (map);
1850 }
1851 
1852 /*
1853 %!shared x
1854 %! x(1).a=1; x(2).a=2; x(1).b=3; x(2).b=3;
1855 %!assert (struct ("a",1, "b",3), x(1))
1856 %!assert (isempty (x([])))
1857 %!assert (isempty (struct ("a",{}, "b",{})))
1858 %!assert (struct ("a",{1,2}, "b",{3,3}), x)
1859 %!assert (struct ("a",{1,2}, "b",3), x)
1860 %!assert (struct ("a",{1,2}, "b",{3}), x)
1861 %!assert (struct ("b",3, "a",{1,2}), x)
1862 %!assert (struct ("b",{3}, "a",{1,2}), x)
1863 %!test x = struct ([]);
1864 %!assert (size (x), [0,0])
1865 %!assert (isstruct (x))
1866 %!assert (isempty (fieldnames (x)))
1867 %!fail ('struct ("a",{1,2},"b",{1,2,3})', 'dimensions of parameter 2 do not match those of parameter 4')
1868 %!error <arguments must occur as "field", VALUE pairs> struct (1,2,3,4)
1869 %!fail ('struct ("1",2,"3")', 'struct: additional arguments must occur as "field", VALUE pairs')
1870 */
1871 
1872 DEFUN (isstruct, args, ,
1873  doc: /* -*- texinfo -*-
1874 @deftypefn {} {} isstruct (@var{x})
1875 Return true if @var{x} is a structure or a structure array.
1876 @seealso{ismatrix, iscell, isa}
1877 @end deftypefn */)
1878 {
1879  if (args.length () != 1)
1880  print_usage ();
1881 
1882  return ovl (args(0).isstruct ());
1883 }
1884 
1885 DEFUN (__fieldnames__, args, ,
1886  doc: /* -*- texinfo -*-
1887 @deftypefn {} {} __fieldnames__ (@var{struct})
1888 @deftypefnx {} {} __fieldnames__ (@var{obj})
1889 Internal function.
1890 
1891 Implements @code{fieldnames()} for structures and Octave objects.
1892 @seealso{fieldnames}
1893 @end deftypefn */)
1894 {
1896 
1897  // Input validation has already been done in fieldnames.m.
1898  octave_value arg = args(0);
1899 
1900  octave_map m = arg.map_value ();
1901 
1902  string_vector keys = m.fieldnames ();
1903 
1904  if (keys.isempty ())
1905  retval = Cell (0, 1);
1906  else
1907  retval = Cell (keys);
1908 
1909  return retval;
1910 }
1911 
1912 DEFUN (isfield, args, ,
1913  doc: /* -*- texinfo -*-
1914 @deftypefn {} {} isfield (@var{x}, "@var{name}")
1915 @deftypefnx {} {} isfield (@var{x}, @var{name})
1916 Return true if the @var{x} is a structure and it includes an element named
1917 @var{name}.
1918 
1919 If @var{name} is a cell array of strings then a logical array of equal
1920 dimension is returned.
1921 @seealso{fieldnames}
1922 @end deftypefn */)
1923 {
1924  if (args.length () != 2)
1925  print_usage ();
1926 
1927  octave_value retval = false;
1928 
1929  if (args(0).isstruct ())
1930  {
1931  octave_map m = args(0).map_value ();
1932 
1933  // FIXME: should this work for all types that can do
1934  // structure reference operations?
1935  if (args(1).is_string ())
1936  {
1937  std::string key = args(1).string_value ();
1938 
1939  retval = m.isfield (key);
1940  }
1941  else if (args(1).iscell ())
1942  {
1943  Cell c = args(1).cell_value ();
1944  boolNDArray bm (c.dims ());
1945  octave_idx_type n = bm.numel ();
1946 
1947  for (octave_idx_type i = 0; i < n; i++)
1948  {
1949  if (c(i).is_string ())
1950  {
1951  std::string key = c(i).string_value ();
1952 
1953  bm(i) = m.isfield (key);
1954  }
1955  else
1956  bm(i) = false;
1957  }
1958 
1959  retval = bm;
1960  }
1961  }
1962 
1963  return retval;
1964 }
1965 
1966 DEFUN (numfields, args, ,
1967  doc: /* -*- texinfo -*-
1968 @deftypefn {} {} numfields (@var{s})
1969 Return the number of fields of the structure @var{s}.
1970 @seealso{fieldnames}
1971 @end deftypefn */)
1972 {
1973  if (args.length () != 1)
1974  print_usage ();
1975 
1976  if (! args(0).isstruct ())
1977  error ("numfields: argument must be a struct");
1978 
1979  return ovl (static_cast<double> (args(0).nfields ()));
1980 }
1981 
1982 /*
1983 ## test isfield
1984 %!test
1985 %! x(3).d=1; x(2).a=2; x(1).b=3; x(2).c=3;
1986 %! assert (isfield (x, "b"));
1987 %!assert (isfield (struct ("a", "1"), "a"))
1988 %!assert (isfield ({1}, "c"), false)
1989 %!assert (isfield (struct ("a", "1"), 10), false)
1990 %!assert (isfield (struct ("a", "b"), "a "), false)
1991 %!assert (isfield (struct ("a", 1, "b", 2), {"a", "c"}), [true, false])
1992 */
1993 
1994 DEFUN (cell2struct, args, ,
1995  doc: /* -*- texinfo -*-
1996 @deftypefn {} {} cell2struct (@var{cell}, @var{fields})
1997 @deftypefnx {} {} cell2struct (@var{cell}, @var{fields}, @var{dim})
1998 Convert @var{cell} to a structure.
1999 
2000 The number of fields in @var{fields} must match the number of elements in
2001 @var{cell} along dimension @var{dim}, that is
2002 @code{numel (@var{fields}) == size (@var{cell}, @var{dim})}. If @var{dim}
2003 is omitted, a value of 1 is assumed.
2004 
2005 @example
2006 @group
2007 A = cell2struct (@{"Peter", "Hannah", "Robert";
2008  185, 170, 168@},
2009  @{"Name","Height"@}, 1);
2010 A(1)
2011  @result{}
2012  @{
2013  Name = Peter
2014  Height = 185
2015  @}
2016 
2017 @end group
2018 @end example
2019 @seealso{struct2cell, cell2mat, struct}
2020 @end deftypefn */)
2021 {
2022  int nargin = args.length ();
2023 
2024  if (nargin < 2 || nargin > 3)
2025  print_usage ();
2026 
2027  if (! args(0).iscell ())
2028  error ("cell2struct: argument CELL must be of type cell");
2029 
2030  if (! (args(1).iscellstr () || args(1).is_char_matrix ()))
2031  error ("cell2struct: FIELDS must be a cell array of strings or a character matrix");
2032 
2033  int dim = 0;
2034 
2035  if (nargin == 3)
2036  {
2037  if (! args(2).is_real_scalar ())
2038  error ("cell2struct: DIM must be a real scalar");
2039 
2040  dim = args(2).int_value () - 1;
2041  }
2042 
2043  if (dim < 0)
2044  error ("cell2struct: DIM must be a valid dimension");
2045 
2046  const Cell vals = args(0).cell_value ();
2047  const Array<std::string> fields = args(1).cellstr_value ();
2048 
2049  octave_idx_type ext = (vals.ndims () > dim ? vals.dims ()(dim) : 1);
2050 
2051  if (ext != fields.numel ())
2052  error ("cell2struct: number of FIELDS does not match dimension");
2053 
2054  int nd = std::max (dim+1, vals.ndims ());
2055  // result dimensions.
2056  dim_vector rdv = vals.dims ().redim (nd);
2057 
2058  assert (ext == rdv(dim));
2059  if (nd == 2)
2060  {
2061  rdv(0) = rdv(1-dim);
2062  rdv(1) = 1;
2063  }
2064  else
2065  {
2066  for (int i = dim + 1; i < nd; i++)
2067  rdv(i-1) = rdv(i);
2068 
2069  rdv.resize (nd-1);
2070  }
2071 
2072  octave_map map (rdv);
2074 
2075  for (octave_idx_type i = 0; i < ext; i++)
2076  {
2077  ia(dim) = i;
2078  map.setfield (fields(i), vals.index (ia).reshape (rdv));
2079  }
2080 
2081  return ovl (map);
2082 }
2083 
2084 /*
2085 ## test cell2struct versus struct2cell
2086 %!test
2087 %! keys = cellstr (char (floor (rand (100,10)*24+65)))';
2088 %! vals = mat2cell (rand (100,1), ones (100,1), 1)';
2089 %! s = struct ([keys; vals]{:});
2090 %! t = cell2struct (vals, keys, 2);
2091 %! assert (s, t);
2092 %! assert (struct2cell (s), vals');
2093 %! assert (fieldnames (s), keys');
2094 
2095 %!assert (cell2struct ({1; 2}, {"a"; "b"}), struct ("a", 1, "b", 2))
2096 
2097 %!assert (cell2struct ({}, {"f"}, 3), struct ("f", {}))
2098 */
2099 
2100 DEFUN (rmfield, args, ,
2101  doc: /* -*- texinfo -*-
2102 @deftypefn {} {@var{sout} =} rmfield (@var{s}, "@var{f}")
2103 @deftypefnx {} {@var{sout} =} rmfield (@var{s}, @var{f})
2104 Return a @emph{copy} of the structure (array) @var{s} with the field @var{f}
2105 removed.
2106 
2107 If @var{f} is a cell array of strings or a character array, remove each of
2108 the named fields.
2109 @seealso{orderfields, fieldnames, isfield}
2110 @end deftypefn */)
2111 {
2112  if (args.length () != 2)
2113  print_usage ();
2114 
2115  octave_map m = args(0).xmap_value ("rmfield: first argument must be a struct");
2116 
2117  octave_value_list fval = Fcellstr (args(1), 1);
2118 
2119  Cell fcell = fval(0).cell_value ();
2120 
2121  for (int i = 0; i < fcell.numel (); i++)
2122  {
2123  std::string key = fcell(i).string_value ();
2124 
2125  if (! m.isfield (key))
2126  error ("rmfield: structure does not contain field %s", key.c_str ());
2127 
2128  m.rmfield (key);
2129  }
2130 
2131  return ovl (m);
2132 }
2133 
2134 /*
2135 ## test rmfield
2136 %!shared x
2137 %! x(3).d=1; x(2).a=2; x(1).b=3; x(2).c=3; x(6).f="abc123";
2138 %!
2139 %!test
2140 %! y = rmfield (x, "c");
2141 %! assert (fieldnames (y), {"d"; "a"; "b"; "f"});
2142 %! assert (size (y), [1, 6]);
2143 %!test
2144 %! y = rmfield (x, {"a", "f"});
2145 %! assert (fieldnames (y), {"d"; "b"; "c"});
2146 %! assert (size (y), [1, 6]);
2147 */
2148 
2149 DEFUN (struct_levels_to_print, args, nargout,
2150  doc: /* -*- texinfo -*-
2151 @deftypefn {} {@var{val} =} struct_levels_to_print ()
2152 @deftypefnx {} {@var{old_val} =} struct_levels_to_print (@var{new_val})
2153 @deftypefnx {} {} struct_levels_to_print (@var{new_val}, "local")
2154 Query or set the internal variable that specifies the number of
2155 structure levels to display.
2156 
2157 When called from inside a function with the @qcode{"local"} option, the
2158 variable is changed locally for the function and any subroutines it calls.
2159 The original variable value is restored when exiting the function.
2160 @seealso{print_struct_array_contents}
2161 @end deftypefn */)
2162 {
2163  return SET_INTERNAL_VARIABLE_WITH_LIMITS (struct_levels_to_print, -1,
2165 }
2166 
2167 DEFUN (print_struct_array_contents, args, nargout,
2168  doc: /* -*- texinfo -*-
2169 @deftypefn {} {@var{val} =} print_struct_array_contents ()
2170 @deftypefnx {} {@var{old_val} =} print_struct_array_contents (@var{new_val})
2171 @deftypefnx {} {} print_struct_array_contents (@var{new_val}, "local")
2172 Query or set the internal variable that specifies whether to print struct
2173 array contents.
2174 
2175 If true, values of struct array elements are printed. This variable does
2176 not affect scalar structures whose elements are always printed. In both
2177 cases, however, printing will be limited to the number of levels specified
2178 by @var{struct_levels_to_print}.
2179 
2180 When called from inside a function with the @qcode{"local"} option, the
2181 variable is changed locally for the function and any subroutines it calls.
2182 The original variable value is restored when exiting the function.
2183 @seealso{struct_levels_to_print}
2184 @end deftypefn */)
2185 {
2186  return SET_INTERNAL_VARIABLE (print_struct_array_contents);
2187 }
void swap_bytes< 4 >(void *ptr)
Definition: byte-swap.h:63
charNDArray max(char d, const charNDArray &m)
Definition: chNDArray.cc:230
octave_idx_type numel(void) const
Number of elements in the array.
Definition: Array.h:377
void make_unique(void)
Definition: Array.h:188
const T * data(void) const
Size of the specified dimension.
Definition: Array.h:581
const dim_vector & dims(void) const
Return a const-reference so that dims ()(i) works efficiently.
Definition: Array.h:453
int ndims(void) const
Size of the specified dimension.
Definition: Array.h:589
Definition: Cell.h:43
Cell reshape(const dim_vector &new_dims) const
Definition: Cell.h:119
Cell index(const octave_value_list &idx, bool resize_ok=false) const
Definition: Cell.cc:171
Vector representing the dimensions (size) of an Array.
Definition: dim-vector.h:95
std::string str(char sep='x') const
Definition: dim-vector.cc:85
octave_idx_type numel(int n=0) const
Number of elements that a matrix with this dimensions would have.
Definition: dim-vector.h:401
void resize(int n, int fill_value=0)
Definition: dim-vector.h:349
octave_idx_type ndims(void) const
Number of dimensions.
Definition: dim-vector.h:334
dim_vector redim(int n) const
Force certain dimensionality, preserving numel ().
Definition: dim-vector.cc:245
static const idx_vector colon
Definition: idx-vector.h:499
octave::refcount< octave_idx_type > count
Definition: ov-base.h:860
void decrement_indent_level(void) const
Definition: ov-base.h:844
void increment_indent_level(void) const
Definition: ov-base.h:841
void indent(std::ostream &os) const
Definition: ov-base.cc:1302
void newline(std::ostream &os) const
Definition: ov-base.cc:1321
void warn_load(const char *type) const
Definition: ov-base.cc:1090
virtual bool is_magic_colon(void) const
Definition: ov-base.h:405
friend class octave_value
Definition: ov-base.h:228
void warn_save(const char *type) const
Definition: ov-base.cc:1099
bool isempty(void) const
Definition: ov-base.h:357
octave_idx_type columns(void) const
Definition: oct-map.h:395
void delete_elements(const idx_vector &i)
Definition: oct-map.cc:1229
octave_scalar_map checkelem(octave_idx_type n) const
Definition: oct-map.h:399
const_iterator cend(void) const
Definition: oct-map.h:322
octave_fields::const_iterator const_iterator
Definition: oct-map.h:315
void setfield(const std::string &key, const Cell &val)
Definition: oct-map.cc:282
octave_idx_type rows(void) const
Definition: oct-map.h:393
octave_idx_type numel(void) const
Definition: oct-map.h:389
const_iterator seek(const std::string &k) const
Definition: oct-map.h:324
void assign(const std::string &k, const Cell &val)
Definition: oct-map.h:365
std::string key(const_iterator p) const
Definition: oct-map.h:326
const_iterator end(void) const
Definition: oct-map.h:319
const_iterator cbegin(void) const
Definition: oct-map.h:321
string_vector fieldnames(void) const
Definition: oct-map.h:353
const Cell & contents(const_iterator p) const
Definition: oct-map.h:331
bool fast_elem_insert(octave_idx_type n, const octave_scalar_map &rhs)
Definition: oct-map.cc:413
octave_idx_type index(const_iterator p) const
Definition: oct-map.h:328
const_iterator cbegin(void) const
Definition: oct-map.h:195
const octave_value & contents(const_iterator p) const
Definition: oct-map.h:205
const_iterator cend(void) const
Definition: oct-map.h:196
void setfield(const std::string &key, const octave_value &val)
Definition: oct-map.cc:190
const_iterator seek(const std::string &k) const
Definition: oct-map.h:198
const_iterator end(void) const
Definition: oct-map.h:193
octave_value getfield(const std::string &key) const
Definition: oct-map.cc:183
string_vector fieldnames(void) const
Definition: oct-map.h:227
std::string key(const_iterator p) const
Definition: oct-map.h:200
bool load_hdf5(octave_hdf5_id loc_id, const char *name)
Definition: ov-struct.cc:1593
bool save_hdf5(octave_hdf5_id loc_id, const char *name, bool save_as_floats)
Definition: ov-struct.cc:1540
octave_value to_array(void)
Definition: ov-struct.cc:1676
bool print_name_tag(std::ostream &os, const std::string &name) const
Definition: ov-struct.cc:1354
dim_vector dims(void) const
Definition: ov-struct.h:222
octave_scalar_map scalar_map_value(void) const
Definition: ov-struct.h:251
static octave_value numeric_conv(const octave_value &val, const std::string &type)
Definition: ov-struct.cc:1183
string_vector map_keys(void) const
Definition: ov-struct.h:253
octave_value do_index_op(const octave_value_list &idx, bool resize_ok=false)
Definition: ov-struct.cc:1268
bool load_ascii(std::istream &is)
Definition: ov-struct.cc:1427
void print(std::ostream &os, bool pr_as_read_syntax=false)
Definition: ov-struct.cc:1294
octave_value subsref(const std::string &type, const std::list< octave_value_list > &idx)
Definition: ov-struct.cc:1109
octave_map map_value(void) const
Definition: ov-struct.h:249
octave_value dotref(const octave_value_list &idx, bool auto_add=false)
Definition: ov-struct.cc:1089
octave_idx_type nfields(void) const
Definition: ov-struct.h:233
bool save_binary(std::ostream &os, bool save_as_floats)
Definition: ov-struct.cc:1467
bool save_ascii(std::ostream &os)
Definition: ov-struct.cc:1391
size_t byte_size(void) const
Definition: ov-struct.cc:1275
bool fast_elem_insert_self(void *where, builtin_type_t btyp) const
Definition: ov-struct.cc:1682
void print_raw(std::ostream &os, bool pr_as_read_syntax=false) const
Definition: ov-struct.cc:1300
mxArray * as_mxArray(void) const
Definition: ov-struct.cc:1643
octave_idx_type numel(void) const
Definition: ov-struct.h:228
bool load_binary(std::istream &is, bool swap, octave::mach_info::float_format fmt)
Definition: ov-struct.cc:1496
octave_scalar_map map
Definition: ov-struct.h:284
std::string edit_display(const float_display_format &fmt, octave_idx_type i, octave_idx_type j) const
Definition: ov-struct.cc:1377
octave_value subsasgn(const std::string &type, const std::list< octave_value_list > &idx, const octave_value &rhs)
Definition: ov-struct.cc:1197
octave_base_value * try_narrowing_conversion(void)
Definition: ov-struct.cc:67
bool fast_elem_insert(octave_idx_type n, const octave_value &x)
Definition: ov-struct.cc:1067
octave_map map
Definition: ov-struct.h:164
static octave_value numeric_conv(const octave_value &val, const std::string &type)
Definition: ov-struct.cc:272
size_t byte_size(void) const
Definition: ov-struct.cc:562
bool load_ascii(std::istream &is)
Definition: ov-struct.cc:739
octave_map map_value(void) const
Definition: ov-struct.h:127
std::string edit_display(const float_display_format &fmt, octave_idx_type i, octave_idx_type j) const
Definition: ov-struct.cc:675
octave_value subsref(const std::string &type, const std::list< octave_value_list > &idx)
Definition: ov-struct.h:71
dim_vector dims(void) const
Definition: ov-struct.h:100
std::string type_name(void) const
Definition: ov-struct.h:168
bool save_ascii(std::ostream &os)
Definition: ov-struct.cc:703
octave_value subsasgn(const std::string &type, const std::list< octave_value_list > &idx, const octave_value &rhs)
Definition: ov-struct.cc:286
bool load_hdf5(octave_hdf5_id loc_id, const char *name)
Definition: ov-struct.cc:973
bool save_hdf5(octave_hdf5_id loc_id, const char *name, bool save_as_floats)
Definition: ov-struct.cc:920
bool print_name_tag(std::ostream &os, const std::string &name) const
Definition: ov-struct.cc:647
octave_value fast_elem_extract(octave_idx_type n) const
Definition: ov-struct.cc:1058
octave_idx_type nfields(void) const
Definition: ov-struct.h:111
bool save_binary(std::ostream &os, bool save_as_floats)
Definition: ov-struct.cc:807
Cell dotref(const octave_value_list &idx, bool auto_add=false)
Definition: ov-struct.cc:78
mxArray * as_mxArray(void) const
Definition: ov-struct.cc:1025
void print(std::ostream &os, bool pr_as_read_syntax=false)
Definition: ov-struct.cc:581
string_vector map_keys(void) const
Definition: ov-struct.h:129
octave_idx_type numel(void) const
Definition: ov-struct.h:106
bool load_binary(std::istream &is, bool swap, octave::mach_info::float_format fmt)
Definition: ov-struct.cc:849
octave_value do_index_op(const octave_value_list &idx, bool resize_ok=false)
Definition: ov-struct.cc:550
void print_raw(std::ostream &os, bool pr_as_read_syntax=false) const
Definition: ov-struct.cc:587
Cell cell_value(void) const
Definition: ovl.h:105
octave_idx_type length(void) const
Definition: ovl.h:113
bool all_scalars(void) const
Definition: ovl.cc:188
bool iscell(void) const
Definition: ov.h:560
Cell xcell_value(const char *fmt,...) const
octave_value subsref(const std::string &type, const std::list< octave_value_list > &idx)
Definition: ov.h:449
octave_map xmap_value(const char *fmt,...) const
void print_with_name(std::ostream &os, const std::string &name) const
Definition: ov.h:1239
static octave_value empty_conv(const std::string &type, const octave_value &rhs=octave_value())
Definition: ov.cc:2887
octave_value undef_subsasgn(const std::string &type, const std::list< octave_value_list > &idx, const octave_value &rhs)
bool is_cs_list(void) const
Definition: ov.h:626
bool isstruct(void) const
Definition: ov.h:605
bool isnull(void) const
Definition: ov.h:632
bool is_zero_by_zero(void) const
Definition: ov.h:515
octave_value storable_value(void) const
void make_unique(void)
Definition: ov.h:336
size_t byte_size(void) const
Definition: ov.h:521
octave_value_list list_value(void) const
bool isobject(void) const
Definition: ov.h:620
octave_map map_value(void) const
bool is_undefined(void) const
Definition: ov.h:554
std::string type_name(void) const
Definition: ov.h:1254
dim_vector dims(void) const
Definition: ov.h:500
octave_value subsasgn(const std::string &type, const std::list< octave_value_list > &idx, const octave_value &rhs)
bool isempty(void) const
Definition: str-vec.h:102
octave_idx_type numel(void) const
Definition: str-vec.h:100
const octave_hdf5_id octave_H5P_DEFAULT
OCTINTERP_API void print_usage(void)
Definition: defun.cc:53
#define DEFUN(name, args_name, nargout_name, doc)
Macro to define a builtin function.
Definition: defun.h:56
void error_with_id(const char *id, const char *fmt,...)
Definition: error.cc:1013
void warning_with_id(const char *id, const char *fmt,...)
Definition: error.cc:1065
void error(const char *fmt,...)
Definition: error.cc:968
#define panic_impossible()
Definition: error.h:380
void warn_empty_index(const std::string &type_name)
Definition: errwarn.cc:336
void err_nonbraced_cs_list_assignment(void)
Definition: errwarn.cc:89
void err_indexed_cs_list(void)
Definition: errwarn.cc:65
QString name
F77_RET_T const F77_DBLE * x
F77_RET_T const F77_DBLE const F77_DBLE * f
octave_hdf5_err hdf5_h5g_iterate(octave_hdf5_id loc_id, const char *name, int *idx, void *operator_data)
Definition: ls-hdf5.cc:1034
bool add_hdf5_data(octave_hdf5_id loc_id, const octave_value &tc, const std::string &name, const std::string &doc, bool mark_global, bool save_as_floats)
Definition: ls-hdf5.cc:1380
std::string read_binary_data(std::istream &is, bool swap, octave::mach_info::float_format fmt, const std::string &filename, bool &global, octave_value &tc, std::string &doc)
bool save_binary_data(std::ostream &os, const octave_value &tc, const std::string &name, const std::string &doc, bool mark_global, bool save_as_floats)
std::string read_text_data(std::istream &is, const std::string &filename, bool &global, octave_value &tc, octave_idx_type count, const bool do_name_validation)
Definition: ls-oct-text.cc:287
std::string extract_keyword(std::istream &is, const char *keyword, const bool next_only)
Definition: ls-oct-text.cc:84
bool save_text_data(std::ostream &os, const octave_value &val_arg, const std::string &name, bool mark_global, int precision)
Definition: ls-oct-text.cc:363
void mxArray
Definition: mex.h:58
T octave_idx_type m
Definition: mx-inlines.cc:773
octave_idx_type n
Definition: mx-inlines.cc:753
T * r
Definition: mx-inlines.cc:773
OCTAVE_IDX_TYPE mwSize
Definition: mxarray.in.h:97
OCTAVE_IDX_TYPE mwIndex
Definition: mxarray.in.h:98
bool valid_identifier(const char *s)
Definition: utils.cc:77
int64_t octave_hdf5_id
#define OCTAVE_LOCAL_BUFFER(T, buf, size)
Definition: oct-locbuf.h:44
#define DEFINE_OV_TYPEID_FUNCTIONS_AND_DATA(t, n, c)
Definition: ov-base.h:180
builtin_type_t
Definition: ov-base.h:72
@ btyp_struct
Definition: ov-base.h:87
OCTAVE_EXPORT octave_value_list Fcellstr(const octave_value_list &args, int)
Definition: ov-cell.cc:1306
static bool scalar(const dim_vector &dims)
Definition: ov-struct.cc:669
static void maybe_warn_invalid_field_name(const std::string &key, const char *who)
Definition: ov-struct.cc:112
static int Vstruct_levels_to_print
Definition: ov-struct.cc:60
static void err_invalid_index_for_assignment(void)
Definition: ov-struct.cc:100
static bool Vprint_struct_array_contents
Definition: ov-struct.cc:64
static void err_invalid_index_type(const std::string &nm, char t)
Definition: ov-struct.cc:106
octave_value::octave_value(const Array< char > &chm, char type) return retval
Definition: ov.cc:811
octave_value_list ovl(const OV_Args &... args)
Construct an octave_value_list with less typing.
Definition: ovl.h:211
bool Vcompact_format
Definition: pr-output.cc:102
octave_value tc
Definition: ls-hdf5.h:114
std::string name
Definition: ls-hdf5.h:108
#define SET_INTERNAL_VARIABLE_WITH_LIMITS(NM, MINVAL, MAXVAL)
Definition: variables.h:109
#define SET_INTERNAL_VARIABLE(NM)
Definition: variables.h:103
F77_RET_T len
Definition: xerbla.cc:61