BRL-CAD
db5_io.c
Go to the documentation of this file.
1 /* D B 5 _ I O . C
2  * BRL-CAD
3  *
4  * Copyright (c) 2004-2014 United States Government as represented by
5  * the U.S. Army Research Laboratory.
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public License
9  * version 2.1 as published by the Free Software Foundation.
10  *
11  * This library is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this file; see the file named COPYING for more
18  * information.
19  */
20 /** @addtogroup db5 */
21 /** @{ */
22 /** @file librt/db5_io.c
23  *
24  * Handle import/export and IO of v5 database objects.
25  *
26  */
27 
28 #include "common.h"
29 
30 #include <stdio.h>
31 #include <string.h>
32 #include <ctype.h>
33 #include "bnetwork.h"
34 
35 #include "bu/endian.h"
36 #include "bu/parse.h"
37 #include "bu/cv.h"
38 #include "vmath.h"
39 #include "bn.h"
40 #include "db5.h"
41 #include "raytrace.h"
42 #include "mater.h"
43 
44 
45 int
46 db5_header_is_valid(const unsigned char *hp)
47 {
48  const struct db5_ondisk_header *odp = (const struct db5_ondisk_header *)hp;
49 
50  if (odp->db5h_magic1 != DB5HDR_MAGIC1) return 0;
51  if (hp[7] != DB5HDR_MAGIC2) return 0;
52 
53  /* hflags */
54  if ((odp->db5h_hflags & DB5HDR_HFLAGS_DLI_MASK) != DB5HDR_HFLAGS_DLI_HEADER_OBJECT)
55  return 0;
56  if ((odp->db5h_hflags & DB5HDR_HFLAGS_NAME_PRESENT)) return 0;
57  if (((odp->db5h_hflags & DB5HDR_HFLAGS_OBJECT_WIDTH_MASK) >> DB5HDR_HFLAGS_OBJECT_WIDTH_SHIFT)
58  != DB5HDR_WIDTHCODE_8BIT) return 0;
59 
60  /* aflags */
61  if ((odp->db5h_aflags & DB5HDR_AFLAGS_ZZZ_MASK) != DB5_ZZZ_UNCOMPRESSED) return 0;
62  if (odp->db5h_aflags & DB5HDR_AFLAGS_PRESENT) return 0;
63  if (((odp->db5h_aflags & DB5HDR_AFLAGS_WIDTH_MASK) >> DB5HDR_AFLAGS_WIDTH_SHIFT)
64  != DB5HDR_WIDTHCODE_8BIT) return 0;
65 
66  /* bflags */
67  if ((odp->db5h_bflags & DB5HDR_BFLAGS_ZZZ_MASK) != DB5_ZZZ_UNCOMPRESSED) return 0;
68  if (odp->db5h_bflags & DB5HDR_BFLAGS_PRESENT) return 0;
69  if (((odp->db5h_bflags & DB5HDR_BFLAGS_WIDTH_MASK) >> DB5HDR_BFLAGS_WIDTH_SHIFT)
70  != DB5HDR_WIDTHCODE_8BIT) return 0;
71 
72  /* major and minor type */
73  if (odp->db5h_major_type != DB5_MAJORTYPE_RESERVED) return 0;
74  if (odp->db5h_minor_type != 0) return 0;
75 
76  /* Check length, known to be 8-bit. Header len=1 8-byte chunk. */
77  if (hp[6] != 1)
78  return 0;
79 
80  return 1; /* valid */
81 }
82 
83 int
85 {
86  if (len <= 255) return DB5HDR_WIDTHCODE_8BIT;
87  if (len <= 65535) return DB5HDR_WIDTHCODE_16BIT;
88  if (len < 0x7ffffffe) return DB5HDR_WIDTHCODE_32BIT;
89  return DB5HDR_WIDTHCODE_64BIT;
90 }
91 
92 
93 size_t
94 db5_decode_length(size_t *lenp, const unsigned char *cp, int format)
95 {
96  *lenp = 0;
97  switch (format) {
98  case DB5HDR_WIDTHCODE_8BIT:
99  *lenp = (*cp);
100  return 1;
101  case DB5HDR_WIDTHCODE_16BIT:
102  *lenp = BU_GSHORT(cp);
103  return 2;
104  case DB5HDR_WIDTHCODE_32BIT:
105  *lenp = BU_GLONG(cp);
106  return 4;
107  case DB5HDR_WIDTHCODE_64BIT:
108  if (sizeof(size_t) >= 8) {
109  *lenp = BU_GLONGLONG(cp);
110  return 8;
111  }
112  bu_bomb("db5_decode_length(): encountered 64-bit length on non-64-bit machine\n");
113  }
114  bu_bomb("db5_decode_length(): unknown width code\n");
115  return 0;
116 }
117 
118 int
119 db5_decode_signed(size_t *lenp, const unsigned char *cp, int format)
120 {
121  switch (format) {
122  case DB5HDR_WIDTHCODE_8BIT:
123  if ((*lenp = (*cp)) & 0x80) *lenp |= ((size_t)-1 ^ 0xFF);
124  return 1;
125  case DB5HDR_WIDTHCODE_16BIT:
126  if ((*lenp = BU_GSHORT(cp)) & 0x8000) *lenp |= ((size_t)-1 ^ 0xFFFF);
127  return 2;
128  case DB5HDR_WIDTHCODE_32BIT:
129  if ((*lenp = BU_GLONG(cp)) & 0x80000000)
130  *lenp |= ((size_t)-1 ^ 0xFFFFFFFF);
131  return 4;
132  case DB5HDR_WIDTHCODE_64BIT:
133  if (sizeof(size_t) >= 8) {
134  *lenp = BU_GLONGLONG(cp);
135  return 8;
136  }
137  bu_bomb("db5_decode_length(): encountered 64-bit length on 32-bit machine\n");
138  }
139  bu_bomb("db5_decode_length(): unknown width code\n");
140  return 0;
141 }
142 
143 
144 /**
145  * Given a value and a variable-width format spec, store it in network
146  * order.
147  *
148  * Returns -
149  * pointer to next available byte.
150  */
151 unsigned char *
153  unsigned char *cp,
154  size_t val,
155  int format)
156 {
157  switch (format) {
158  case DB5HDR_WIDTHCODE_8BIT:
159  *cp = (unsigned char)val & 0xFF;
160  return cp + sizeof(unsigned char);
161  case DB5HDR_WIDTHCODE_16BIT:
162  *(uint16_t *)&cp[0] = htons((uint16_t)val);
163  return cp + sizeof(uint16_t);
164  case DB5HDR_WIDTHCODE_32BIT:
165  *(uint32_t *)&cp[0] = htonl((uint32_t)val);
166  return cp + sizeof(uint32_t);
167  case DB5HDR_WIDTHCODE_64BIT:
168  *(uint64_t *)&cp[0] = htonll((uint64_t)val);
169  return cp + sizeof(uint64_t);
170  }
171  bu_bomb("db5_encode_length(): unknown width code\n");
172  return NULL;
173 }
174 
175 
176 /**
177  * Returns -
178  * 0 on success
179  * -1 on error
180  */
181 int
182 db5_crack_disk_header(struct db5_raw_internal *rip, const unsigned char *cp)
183 {
184  if (cp[0] != DB5HDR_MAGIC1) {
185  bu_log("db5_crack_disk_header() bad magic1 -- database has become corrupted\n expected x%x, got x%x\n",
186  DB5HDR_MAGIC1, cp[0]);
187  if (cp[0] == 'I') {
188  bu_log ("Concatenation of different database versions detected.\n");
189  bu_log ("Run 'dbupgrade' on all databases before concatenation (cat command).\n");
190  }
191  return 0;
192  }
193 
194  /* hflags */
195  rip->h_dli = (cp[1] & DB5HDR_HFLAGS_DLI_MASK);
196  rip->h_object_width = (cp[1] & DB5HDR_HFLAGS_OBJECT_WIDTH_MASK) >>
197  DB5HDR_HFLAGS_OBJECT_WIDTH_SHIFT;
198  rip->h_name_present = (cp[1] & DB5HDR_HFLAGS_NAME_PRESENT);
199  rip->h_name_hidden = (cp[1] & DB5HDR_HFLAGS_HIDDEN_OBJECT);
200  rip->h_name_width = (cp[1] & DB5HDR_HFLAGS_NAME_WIDTH_MASK) >>
201  DB5HDR_HFLAGS_NAME_WIDTH_SHIFT;
202 
203  /* aflags */
204  rip->a_width = (cp[2] & DB5HDR_AFLAGS_WIDTH_MASK) >>
205  DB5HDR_AFLAGS_WIDTH_SHIFT;
206  rip->a_present = (cp[2] & DB5HDR_AFLAGS_PRESENT);
207  rip->a_zzz = (cp[2] & DB5HDR_AFLAGS_ZZZ_MASK);
208 
209  /* bflags */
210  rip->b_width = (cp[3] & DB5HDR_BFLAGS_WIDTH_MASK) >>
211  DB5HDR_BFLAGS_WIDTH_SHIFT;
212  rip->b_present = (cp[3] & DB5HDR_BFLAGS_PRESENT);
213  rip->b_zzz = (cp[3] & DB5HDR_BFLAGS_ZZZ_MASK);
214 
215  rip->major_type = cp[4];
216  rip->minor_type = cp[5];
217 
218  if (RT_G_DEBUG&DEBUG_DB) bu_log("db5_crack_disk_header()\n\
219  h_dli=%d, h_object_width=%d, h_name_present=%d, h_name_width=%d, \n\
220  a_width=%d, a_present=%d, a_zzz=%d, \n\
221  b_width=%d, b_present=%d, b_zzz=%d, major=%d, minor=%d\n",
222  rip->h_dli,
223  rip->h_object_width,
224  rip->h_name_present,
225  rip->h_name_width,
226  rip->a_width,
227  rip->a_present,
228  rip->a_zzz,
229  rip->b_width,
230  rip->b_present,
231  rip->b_zzz,
232  rip->major_type,
233  rip->minor_type);
234 
235  return 0;
236 }
237 
238 
239 /**
240  * Returns -
241  * on success, pointer to first unused byte
242  * NULL, on error
243  */
244 const unsigned char *
245 db5_get_raw_internal_ptr(struct db5_raw_internal *rip, const unsigned char *ip)
246 {
247  const unsigned char *cp = ip;
248 
249  if (db5_crack_disk_header(rip, cp) < 0) return NULL;
250  cp += sizeof(struct db5_ondisk_header);
251 
252  cp += db5_decode_length(&rip->object_length, cp, rip->h_object_width);
253  rip->object_length <<= 3; /* cvt 8-byte chunks to byte count */
254 
255  if ((size_t)rip->object_length < sizeof(struct db5_ondisk_header)) {
256  bu_log("db5_get_raw_internal_ptr(): object_length=%ld is too short, database is corrupted\n",
257  rip->object_length);
258  return NULL;
259  }
260 
261  /* Verify trailing magic number */
262  if (ip[rip->object_length-1] != DB5HDR_MAGIC2) {
263  bu_log("db5_get_raw_internal_ptr() bad magic2 -- database has become corrupted.\n expected x%x, got x%x\n",
264  DB5HDR_MAGIC2, ip[rip->object_length-1]);
265  return NULL;
266  }
267 
268  BU_EXTERNAL_INIT(&rip->name);
269  BU_EXTERNAL_INIT(&rip->body);
270  BU_EXTERNAL_INIT(&rip->attributes);
271 
272  /* Grab name, if present */
273  if (rip->h_name_present) {
274  cp += db5_decode_length(&rip->name.ext_nbytes,
275  cp, rip->h_name_width);
276  rip->name.ext_buf = (uint8_t *)cp; /* discard const */
277  cp += rip->name.ext_nbytes;
278  }
279 
280  /* Point to attributes, if present */
281  if (rip->a_present) {
282  cp += db5_decode_length(&rip->attributes.ext_nbytes,
283  cp, rip->a_width);
284  rip->attributes.ext_buf = (uint8_t *)cp; /* discard const */
285 #if defined(USE_BINARY_ATTRIBUTES)
286  rip->attributes.widcode = rip->a_width;
287 #endif
288  cp += rip->attributes.ext_nbytes;
289  }
290 
291  /* Point to body, if present */
292  if (rip->b_present) {
293  cp += db5_decode_length(&rip->body.ext_nbytes,
294  cp, rip->b_width);
295  rip->body.ext_buf = (uint8_t *)cp; /* discard const */
296  cp += rip->body.ext_nbytes;
297  }
298 
299  rip->buf = NULL; /* no buffer needs freeing */
300 
301  return ip + rip->object_length;
302 }
303 
304 int
305 db5_get_raw_internal_fp(struct db5_raw_internal *rip, FILE *fp)
306 {
307  struct db5_ondisk_header header;
308  unsigned char lenbuf[8];
309  int count = 0;
310  size_t used;
311  size_t want, got;
312  size_t dlen;
313  unsigned char *cp;
314 
315  if (fread((unsigned char *)&header, sizeof header, 1, fp) != 1) {
316  if (feof(fp)) return -1;
317  bu_log("db5_get_raw_internal_fp(): fread header error\n");
318  return -2;
319  }
320  if (db5_crack_disk_header(rip, (unsigned char *)&header) < 0)
321  return -2;
322  used = sizeof(header);
323 
324  switch (rip->h_object_width) {
325  case DB5HDR_WIDTHCODE_8BIT:
326  count = 1;
327  break;
328  case DB5HDR_WIDTHCODE_16BIT:
329  count = 2;
330  break;
331  case DB5HDR_WIDTHCODE_32BIT:
332  count = 4;
333  break;
334  case DB5HDR_WIDTHCODE_64BIT:
335  count = 8;
336  }
337  if (fread(lenbuf, count, 1, fp) != 1) {
338  perror("fread");
339  bu_log("db5_get_raw_internal_fp(): fread lenbuf error\n");
340  return -2;
341  }
342 
343  dlen = db5_decode_length(&rip->object_length, lenbuf, rip->h_object_width);
344  if (dlen < 1 || dlen > sizeof(size_t)) {
345  bu_log("db5_get_raw_internal_fp(): Error decoding object length (got %zd)\n", dlen);
346  return -1;
347  }
348  used += dlen;
349 
350  /* verify the length won't overflow before we <<=3 it */
351  if (rip->object_length > UINTPTR_MAX>>3) {
352  bu_log("db5_get_raw_internal_fp(): bad length read\n");
353  return -1;
354  }
355  rip->object_length <<= 3; /* cvt 8-byte chunks to byte count */
356 
357  if (rip->object_length < sizeof(struct db5_ondisk_header) || rip->object_length < used) {
358  bu_log("db5_get_raw_internal_fp(): object_length=%ld is too short, database is corrupted\n",
359  rip->object_length);
360  return -1;
361  }
362 
363  /* Now that we finally know how large the object is, get it all */
364  rip->buf = (unsigned char *)bu_malloc(rip->object_length, "raw v5 object");
365 
366  *((struct db5_ondisk_header *)rip->buf) = header; /* struct copy */
367  memcpy(rip->buf+sizeof(header), lenbuf, count);
368 
369  cp = rip->buf + used;
370  want = rip->object_length - used;
371 
372  if ((got = fread(cp, 1, want, fp)) != want) {
373  bu_log("db5_get_raw_internal_fp(): Database is too short, want=%ld, got=%ld\n",
374  want, got);
375  return -2;
376  }
377 
378  /* Verify trailing magic number */
379  if (rip->buf[rip->object_length-1] != DB5HDR_MAGIC2) {
380  bu_log("db5_get_raw_internal_fp(): bad magic2 -- database has become corrupted.\n expected x%x, got x%x\n",
381  DB5HDR_MAGIC2, rip->buf[rip->object_length-1]);
382  return -2;
383  }
384 
385  BU_EXTERNAL_INIT(&rip->name);
386  BU_EXTERNAL_INIT(&rip->body);
387  BU_EXTERNAL_INIT(&rip->attributes);
388 
389  /* Grab name, if present */
390  if (rip->h_name_present) {
391  cp += db5_decode_length(&rip->name.ext_nbytes,
392  cp, rip->h_name_width);
393  rip->name.ext_buf = (uint8_t *)cp; /* discard const */
394  cp += rip->name.ext_nbytes;
395  }
396 
397  /* Point to attributes, if present */
398  if (rip->a_present) {
399  cp += db5_decode_length(&rip->attributes.ext_nbytes,
400  cp, rip->a_width);
401  rip->attributes.ext_buf = (uint8_t *)cp; /* discard const */
402 #if defined(USE_BINARY_ATTRIBUTES)
403  rip->attributes.widcode = rip->a_width;
404 #endif
405  cp += rip->attributes.ext_nbytes;
406  }
407 
408  /* Point to body, if present */
409  if (rip->b_present) {
410  cp += db5_decode_length(&rip->body.ext_nbytes,
411  cp, rip->b_width);
412  rip->body.ext_buf = (uint8_t *)cp; /* discard const */
413  cp += rip->body.ext_nbytes;
414  }
415 
416  return 0; /* success */
417 }
418 
419 void
421  struct bu_external *out,
422  int dli,
423  const char *name,
424  const unsigned char hidden,
425  const struct bu_external *attrib,
426  const struct bu_external *body,
427  int major,
428  int minor,
429  int a_zzz,
430  int b_zzz)
431 {
432  struct db5_ondisk_header *odp;
433  register unsigned char *cp;
434  long namelen = 0;
435  size_t need;
436  int h_width, n_width, a_width, b_width;
437  long togo;
438 
439  /*
440  * First, compute an upper bound on the size buffer needed.
441  * Over-estimate on the length fields just to keep it simple.
442  */
443  need = sizeof(struct db5_ondisk_header);
444  need += 8; /* for object_length */
445  if (name) {
446  namelen = (long)strlen(name) + 1; /* includes null */
447  if (namelen > 1) {
448  n_width = db5_select_length_encoding(namelen);
449  need += namelen + DB5_ENC_LEN(n_width);
450  } else {
451  name = NULL;
452  namelen = 0;
453  n_width = 0;
454  }
455  } else {
456  n_width = 0;
457  }
458  if (attrib) {
459  BU_CK_EXTERNAL(attrib);
460  if (attrib->ext_nbytes > 0) {
461  a_width = db5_select_length_encoding(attrib->ext_nbytes);
462  need += attrib->ext_nbytes + DB5_ENC_LEN(a_width);
463  } else {
464  attrib = NULL;
465  a_width = 0;
466  }
467  } else {
468  a_width = 0;
469  }
470  if (body) {
471  BU_CK_EXTERNAL(body);
472  if (body->ext_nbytes > 0) {
473  b_width = db5_select_length_encoding(body->ext_nbytes);
474  need += body->ext_nbytes + DB5_ENC_LEN(b_width);
475  } else {
476  body = NULL;
477  b_width = 0;
478  }
479  } else {
480  b_width = 0;
481  }
482  need += 8; /* pad and magic2 */
483 
484  /* Allocate the buffer for the combined external representation */
485  BU_EXTERNAL_INIT(out);
486  out->ext_buf = (uint8_t *)bu_malloc(need, "external object3");
487  out->ext_nbytes = need; /* will be trimmed, below */
488 
489  /* Determine encoding for the header length field */
490  h_width = db5_select_length_encoding((need+7)>>3);
491 
492  /* prepare combined external object */
493  odp = (struct db5_ondisk_header *)out->ext_buf;
494  odp->db5h_magic1 = DB5HDR_MAGIC1;
495 
496  /* hflags */
497  odp->db5h_hflags = (h_width << DB5HDR_HFLAGS_OBJECT_WIDTH_SHIFT) |
498  (dli & DB5HDR_HFLAGS_DLI_MASK);
499  if (name) {
500  odp->db5h_hflags |= DB5HDR_HFLAGS_NAME_PRESENT |
501  (n_width << DB5HDR_HFLAGS_NAME_WIDTH_SHIFT);
502 
503  }
504  if (hidden) {
505  odp->db5h_hflags |= DB5HDR_HFLAGS_HIDDEN_OBJECT;
506  }
507 
508  /* aflags */
509  odp->db5h_aflags = a_width << DB5HDR_AFLAGS_WIDTH_SHIFT;
510  if (attrib) odp->db5h_aflags |= DB5HDR_AFLAGS_PRESENT;
511  odp->db5h_aflags |= a_zzz & DB5HDR_AFLAGS_ZZZ_MASK;
512 
513  /* bflags */
514  odp->db5h_bflags = b_width << DB5HDR_BFLAGS_WIDTH_SHIFT;
515  if (body) odp->db5h_bflags |= DB5HDR_BFLAGS_PRESENT;
516  odp->db5h_bflags |= b_zzz & DB5HDR_BFLAGS_ZZZ_MASK;
517 
518  if (a_zzz || b_zzz) bu_bomb("db5_export_object3: compression not supported yet\n");
519 
520  /* Object_Type */
521  odp->db5h_major_type = major;
522  odp->db5h_minor_type = minor;
523 
524  /* Build up the rest of the record */
525  cp = ((unsigned char *)out->ext_buf) + sizeof(struct db5_ondisk_header);
526  cp = db5_encode_length(cp, 7L, h_width); /* will be replaced below */
527 
528  if (name) {
529  cp = db5_encode_length(cp, namelen, n_width);
530  memcpy(cp, name, namelen); /* includes null */
531  cp += namelen;
532  }
533 
534  if (attrib) {
535  /* minimum buffer length is a one byte attribute name, followed by a NULL name termination,
536  * followed by no bytes (for an empty value), followed by a NULL value termination,
537  * followed by a NULL attribute-value termination. Minimum is 4 bytes
538  */
539  BU_ASSERT_PTR(attrib->ext_nbytes, >=, 4);
540  cp = db5_encode_length(cp, attrib->ext_nbytes, a_width);
541  memcpy(cp, attrib->ext_buf, attrib->ext_nbytes);
542  cp += attrib->ext_nbytes;
543  }
544 
545  if (body) {
546  cp = db5_encode_length(cp, body->ext_nbytes, b_width);
547  memcpy(cp, body->ext_buf, body->ext_nbytes);
548  cp += body->ext_nbytes;
549  }
550 
551  togo = cp - ((unsigned char *)out->ext_buf) + 1;
552  togo &= 7;
553  if (togo != 0) {
554  togo = 8 - togo;
555  while (togo-- > 0) *cp++ = '\0';
556  }
557  *cp++ = DB5HDR_MAGIC2;
558 
559  /* Verify multiple of 8 */
560  togo = cp - ((unsigned char *)out->ext_buf);
561  BU_ASSERT_LONG(togo&7, ==, 0);
562 
563  /* Finally, go back to the header and write the actual object length */
564  cp = ((unsigned char *)out->ext_buf) + sizeof(struct db5_ondisk_header);
565  (void)db5_encode_length(cp, togo>>3, h_width);
566 
567  out->ext_nbytes = togo;
568  BU_ASSERT_LONG(out->ext_nbytes, >=, 8);
569 }
570 
571 void
572 db5_make_free_object_hdr(struct bu_external *ep, size_t length)
573 {
574  struct db5_ondisk_header *odp;
575  int h_width;
576  unsigned char *cp;
577 
578  BU_CK_EXTERNAL(ep);
579 
580  BU_ASSERT_SIZE_T(length, >=, 8);
581  BU_ASSERT_SIZE_T(length&7, ==, 0);
582 
583  /* Reserve enough space to hold any free header, even w/64-bit len */
584  ep->ext_nbytes = 8+8;
585  ep->ext_buf = (uint8_t *)bu_calloc(1, ep->ext_nbytes, "db5_make_free_object_hdr");
586 
587  /* Determine encoding for the header length field */
588  h_width = db5_select_length_encoding(length>>3);
589 
590  /* prepare header of external object */
591  odp = (struct db5_ondisk_header *)ep->ext_buf;
592  odp->db5h_magic1 = DB5HDR_MAGIC1;
593  odp->db5h_hflags = (h_width << DB5HDR_HFLAGS_OBJECT_WIDTH_SHIFT) |
594  DB5HDR_HFLAGS_DLI_FREE_STORAGE;
595 
596  cp = ((unsigned char *)ep->ext_buf) + sizeof(struct db5_ondisk_header);
597  db5_encode_length(cp, length>>3, h_width);
598 }
599 
600 void
601 db5_make_free_object(struct bu_external *ep, size_t length)
602 {
603  struct db5_ondisk_header *odp;
604  int h_width;
605  unsigned char *cp;
606 
607  BU_CK_EXTERNAL(ep);
608 
609  BU_ASSERT_SIZE_T(length, >=, 8);
610  BU_ASSERT_SIZE_T(length&7, ==, 0);
611 
612  ep->ext_buf = (uint8_t *)bu_calloc(1, length, "db5_make_free_object");
613  ep->ext_nbytes = length;
614 
615  /* Determine encoding for the header length field */
616  h_width = db5_select_length_encoding(length>>3);
617 
618  /* prepare combined external object */
619  odp = (struct db5_ondisk_header *)ep->ext_buf;
620  odp->db5h_magic1 = DB5HDR_MAGIC1;
621  odp->db5h_hflags = (h_width << DB5HDR_HFLAGS_OBJECT_WIDTH_SHIFT) |
622  DB5HDR_HFLAGS_DLI_FREE_STORAGE;
623 
624  cp = ((unsigned char *)ep->ext_buf) + sizeof(struct db5_ondisk_header);
625  db5_encode_length(cp, length>>3, h_width);
626 
627  cp = ((unsigned char *)ep->ext_buf) + length-1;
628  *cp = DB5HDR_MAGIC2;
629 }
630 
631 int
633  struct bu_external *ext,
634  const char *name,
635  const struct rt_db_internal *ip,
636  double conv2mm,
637  struct db_i *dbip,
638  struct resource *resp,
639  const int major)
640 {
641  struct bu_external attributes;
642  struct bu_external body;
643  int minor;
644  int ret;
645 
646  /* check inputs */
647  if (!name) {
648  bu_log("rt_db_cvt_to_external5 expecting non-NULL name parameter\n");
649  return -1;
650  }
651  RT_CK_DB_INTERNAL(ip);
652  if (dbip) RT_CK_DBI(dbip); /* may be null */
653 
654  if (resp) {
655  RT_CK_RESOURCE(resp);
656  } else {
657  /* needed for call into functab */
658  resp = &rt_uniresource;
659  }
660 
661  /* prepare output */
662  BU_EXTERNAL_INIT(ext);
663  BU_EXTERNAL_INIT(&body);
664  BU_EXTERNAL_INIT(&attributes);
665 
666  minor = ip->idb_type; /* XXX not necessarily v5 numbers. */
667 
668  /* Scale change on export is 1.0 -- no change */
669  ret = -1;
670  if (ip->idb_meth && ip->idb_meth->ft_export5) {
671  ret = ip->idb_meth->ft_export5(&body, ip, conv2mm, dbip, resp);
672  }
673  if (ret < 0) {
674  bu_log("rt_db_cvt_to_external5(%s): ft_export5 failure\n",
675  name);
676  bu_free_external(&body);
677  return -1; /* FAIL */
678  }
679  BU_CK_EXTERNAL(&body);
680 
681  /* If present, convert attributes to on-disk format. */
682  if (ip->idb_avs.magic == BU_AVS_MAGIC) {
683  db5_export_attributes(&attributes, &ip->idb_avs);
684  BU_CK_EXTERNAL(&attributes);
685  }
686 
687  /* serialize the object with attributes */
688  db5_export_object3(ext, DB5HDR_HFLAGS_DLI_APPLICATION_DATA_OBJECT,
689  name, 0, &attributes, &body,
690  major, minor,
691  DB5_ZZZ_UNCOMPRESSED, DB5_ZZZ_UNCOMPRESSED);
692  BU_CK_EXTERNAL(ext);
693 
694  /* cleanup */
695  bu_free_external(&body);
696  bu_free_external(&attributes);
697 
698  return 0; /* OK */
699 }
700 
701 int
702 db_wrap_v5_external(struct bu_external *ep, const char *name)
703 {
704  struct db5_raw_internal raw;
705  struct bu_external tmp;
706 
707  BU_CK_EXTERNAL(ep);
708 
709  /* Crack the external form into parts */
710  if (db5_get_raw_internal_ptr(&raw, (unsigned char *)ep->ext_buf) == NULL) {
711  bu_log("db_put_external5(%s) failure in db5_get_raw_internal_ptr()\n",
712  name);
713  return -1;
714  }
715  BU_ASSERT_LONG(raw.h_dli, ==, DB5HDR_HFLAGS_DLI_APPLICATION_DATA_OBJECT);
716 
717  /* See if name needs to be changed */
718  if (raw.name.ext_buf == NULL || !BU_STR_EQUAL(name, (const char *)raw.name.ext_buf)) {
719  /* Name needs to be changed. Create new external form.
720  * Make temporary copy so input isn't smashed
721  * as new external object is constructed.
722  */
723  tmp = *ep; /* struct copy */
724  BU_EXTERNAL_INIT(ep);
725 
727  DB5HDR_HFLAGS_DLI_APPLICATION_DATA_OBJECT,
728  name,
729  raw.h_name_hidden,
730  &raw.attributes,
731  &raw.body,
732  raw.major_type, raw.minor_type,
733  raw.a_zzz, raw.b_zzz);
734  /* 'raw' is invalid now, 'ep' has new external form. */
735  bu_free_external(&tmp);
736  return 0;
737  }
738 
739  /* No changes needed, input object is properly named */
740  return 0;
741 }
742 
743 int
744 db_put_external5(struct bu_external *ep, struct directory *dp, struct db_i *dbip)
745 {
746  RT_CK_DBI(dbip);
747  RT_CK_DIR(dp);
748  BU_CK_EXTERNAL(ep);
749 
750  if (RT_G_DEBUG&DEBUG_DB) bu_log("db_put_external5(%s) ep=%p, dbip=%p, dp=%p\n",
751  dp->d_namep, (void *)ep, (void *)dbip, (void *)dp);
752 
753  if (dbip->dbi_read_only) {
754  bu_log("db_put_external5(%s): READ-ONLY file\n",
755  dbip->dbi_filename);
756  return -1;
757  }
758 
759  BU_ASSERT_LONG(dbip->dbi_version, ==, 5);
760 
761  /* First, change the name. */
762  if (db_wrap_v5_external(ep, dp->d_namep) < 0) {
763  bu_log("db_put_external5(%s) failure in db_wrap_v5_external()\n",
764  dp->d_namep);
765  return -1;
766  }
767 
768  /* Second, obtain storage for final object */
769  if (ep->ext_nbytes != dp->d_len || (size_t)dp->d_addr == (size_t)RT_DIR_PHONY_ADDR) {
770  if (db5_realloc(dbip, dp, ep) < 0) {
771  bu_log("db_put_external(%s) db_realloc5() failed\n", dp->d_namep);
772  return -5;
773  }
774  }
775  BU_ASSERT_LONG(ep->ext_nbytes, ==, dp->d_len);
776 
777  if (dp->d_flags & RT_DIR_INMEM) {
778  memcpy(dp->d_un.ptr, (char *)ep->ext_buf, ep->ext_nbytes);
779  return 0;
780  }
781 
782  if (db_write(dbip, (char *)ep->ext_buf, ep->ext_nbytes, dp->d_addr) < 0) {
783  return -1;
784  }
785  return 0;
786 }
787 
788 int
790  struct directory *dp,
791  struct db_i *dbip,
792  struct rt_db_internal *ip,
793  struct resource *resp,
794  const int major)
795 {
796  struct bu_external ext;
797 
798  RT_CK_DIR(dp);
799  RT_CK_DBI(dbip);
800  RT_CK_DB_INTERNAL(ip);
801  BU_ASSERT_LONG(dbip->dbi_version, ==, 5);
802 
803  if (resp)
804  RT_CK_RESOURCE(resp);
805 
806  BU_EXTERNAL_INIT(&ext);
807  if (rt_db_cvt_to_external5(&ext, dp->d_namep, ip, 1.0, dbip, resp, major) < 0) {
808  bu_log("rt_db_put_internal5(%s): export failure\n",
809  dp->d_namep);
810  goto fail;
811  }
812  BU_CK_EXTERNAL(&ext);
813 
814  if (ext.ext_nbytes != dp->d_len || dp->d_addr == RT_DIR_PHONY_ADDR) {
815  if (db5_realloc(dbip, dp, &ext) < 0) {
816  bu_log("rt_db_put_internal5(%s) db_realloc5() failed\n", dp->d_namep);
817  goto fail;
818  }
819  }
820  BU_ASSERT_LONG(ext.ext_nbytes, ==, dp->d_len);
821 
822  if (dp->d_flags & RT_DIR_INMEM) {
823  memcpy(dp->d_un.ptr, ext.ext_buf, ext.ext_nbytes);
824  goto ok;
825  }
826 
827  if (db_write(dbip, (char *)ext.ext_buf, ext.ext_nbytes, dp->d_addr) < 0) {
828  goto fail;
829  }
830 ok:
831  bu_free_external(&ext);
833  return 0; /* OK */
834 
835 fail:
836  bu_free_external(&ext);
838  return -2; /* FAIL */
839 }
840 
841 
842 /* FIXME: should have gone away with v6. needed now to pass the minor_type down during read */
843 extern int rt_binunif_import5_minor_type(struct rt_db_internal *, const struct bu_external *, const mat_t, const struct db_i *, struct resource *, int);
844 
845 /**
846  * Given an object in external form, convert it to internal form. The
847  * caller is responsible for freeing the external form.
848  *
849  * Returns -
850  * <0 On error
851  * id On success.
852  */
853 int
855  struct rt_db_internal *ip,
856  const struct bu_external *ep,
857  const char *name,
858  const struct db_i *dbip,
859  const mat_t mat,
860  struct resource *resp)
861 {
862  register int id;
863  struct db5_raw_internal raw;
864  int ret;
865 
866  BU_CK_EXTERNAL(ep);
867  RT_CK_DB_INTERNAL(ip);
868  RT_CK_DBI(dbip);
869 
870  if (resp) {
871  RT_CK_RESOURCE(resp);
872  } else {
873  /* needed for call into functab */
874  resp = &rt_uniresource;
875  }
876 
877  BU_ASSERT_LONG(dbip->dbi_version, ==, 5);
878 
879  if (db5_get_raw_internal_ptr(&raw, ep->ext_buf) == NULL) {
880  bu_log("rt_db_external5_to_internal5(%s): import failure\n",
881  name);
882  return -3;
883  }
884 
885  if ((raw.major_type == DB5_MAJORTYPE_BRLCAD)
886  ||(raw.major_type == DB5_MAJORTYPE_BINARY_UNIF)) {
887  /* As a convenience to older ft_import routines */
888  if (mat == NULL) mat = bn_mat_identity;
889  } else {
890  bu_log("rt_db_external5_to_internal5(%s): unable to import non-BRL-CAD object, major=%d minor=%d\n",
891  name, raw.major_type, raw.minor_type);
892  return -1; /* FAIL */
893  }
894 
895  if (ip->idb_avs.magic != BU_AVS_MAGIC) {
897  }
898 
899  /* If attributes are present in the object, make them available
900  * in the internal form.
901  */
902  if (raw.attributes.ext_buf) {
903  if (db5_import_attributes(&ip->idb_avs, &raw.attributes) < 0) {
904  bu_log("rt_db_external5_to_internal5(%s): mal-formed attributes in database\n",
905  name);
906  return -8;
907  }
908 
909  (void)db5_standardize_avs(&ip->idb_avs);
910  }
911 
912  if (!raw.body.ext_buf) {
913  bu_log("rt_db_external5_to_internal5(%s): object has no body\n",
914  name);
915  return -4;
916  }
917 
918  /* FIXME: This is a temporary kludge accommodating dumb binunifs
919  * that don't export their minor type or have table entries for
920  * all their types. (this gets pushed up when a functab wrapper is
921  * created)
922  */
923  switch (raw.major_type) {
924  case DB5_MAJORTYPE_BRLCAD:
925  id = raw.minor_type; break;
926  case DB5_MAJORTYPE_BINARY_UNIF:
927  id = ID_BINUNIF; break;
928  default:
929  bu_log("rt_db_external5_to_internal5(%s): don't yet handle major_type %d\n", name, raw.major_type);
930  return -1;
931  }
932 
933  /* ip has already been initialized, and should not be re-inited */
934  ret = -1;
935  if (id == ID_BINUNIF) {
936  /* FIXME: binunif export needs to write out minor_type so
937  * this isn't needed, but breaks compatibility. slate for
938  * v6.
939  */
940  ret = rt_binunif_import5_minor_type(ip, &raw.body, mat, dbip, resp, raw.minor_type);
941  } else if (OBJ[id].ft_import5) {
942  ret = OBJ[id].ft_import5(ip, &raw.body, mat, dbip, resp);
943  }
944  if (ret < 0) {
945  bu_log("rt_db_external5_to_internal5(%s): import failure\n",
946  name);
948  return -1; /* FAIL */
949  }
950  /* Don't free &raw.body */
951 
952  RT_CK_DB_INTERNAL(ip);
953  ip->idb_major_type = raw.major_type;
954  ip->idb_minor_type = raw.minor_type;
955  ip->idb_meth = &OBJ[id];
956 
957  return id; /* OK */
958 }
959 
960 int
962  struct rt_db_internal *ip,
963  const struct directory *dp,
964  const struct db_i *dbip,
965  const mat_t mat,
966  struct resource *resp)
967 {
968  struct bu_external ext = BU_EXTERNAL_INIT_ZERO;
969  int ret;
970 
972  if (resp) {
973  RT_CK_RESOURCE(resp);
974  }
975 
976  BU_ASSERT_LONG(dbip->dbi_version, ==, 5);
977 
978  if (db_get_external(&ext, dp, dbip) < 0)
979  return -2; /* FAIL */
980 
981  ret = rt_db_external5_to_internal5(ip, &ext, dp->d_namep, dbip, mat, resp);
982  bu_free_external(&ext);
983  return ret;
984 }
985 
986 
987 void
988 db5_export_color_table(struct bu_vls *ostr, struct db_i *dbip)
989 {
990  BU_CK_VLS(ostr);
991  RT_CK_DBI(dbip);
992  rt_vls_color_map(ostr);
993 }
994 
995 void
997 {
998  char *sp = cp;
999  int low, high, r, g, b;
1000 
1001  while ((sp = strchr(sp, '{')) != NULL) {
1002  sp++;
1003  if (sscanf(sp, "%d %d %d %d %d", &low, &high, &r, &g, &b) != 5) break;
1004  rt_color_addrec(low, high, r, g, b, MATER_NO_ADDR);
1005  }
1006 }
1007 
1008 int
1010 {
1011  struct bu_vls str = BU_VLS_INIT_ZERO;
1012  int ret;
1013 
1014  RT_CK_DBI(dbip);
1015  BU_ASSERT_LONG(dbip->dbi_version, ==, 5);
1016 
1017  db5_export_color_table(&str, dbip);
1018 
1019  ret = db5_update_attribute(DB5_GLOBAL_OBJECT_NAME,
1020  "regionid_colortable", bu_vls_addr(&str), dbip);
1021 
1022  bu_vls_free(&str);
1023  return ret;
1024 }
1025 
1026 int
1027 db5_get_attributes(const struct db_i *dbip, struct bu_attribute_value_set *avs, const struct directory *dp)
1028 {
1029  struct bu_external ext = BU_EXTERNAL_INIT_ZERO;
1030  struct db5_raw_internal raw;
1031 
1032  RT_CK_DBI(dbip);
1033 
1034  if (dbip->dbi_version < 5)
1035  return 0; /* not an error, just no attributes */
1036 
1037  RT_CK_DIR(dp);
1038 
1039  BU_AVS_INIT(avs);
1040 
1041  if (db_get_external(&ext, dp, dbip) < 0)
1042  return -1; /* FAIL */
1043 
1044  if (db5_get_raw_internal_ptr(&raw, ext.ext_buf) == NULL) {
1045  bu_free_external(&ext);
1046  return -2;
1047  }
1048 
1049  if (raw.attributes.ext_buf) {
1050  if (db5_import_attributes(avs, &raw.attributes) < 0) {
1051  bu_free_external(&ext);
1052  return -3;
1053  }
1054  }
1055 
1056  bu_free_external(&ext);
1057  return 0;
1058 }
1059 
1060 
1061 /** @} */
1062 /*
1063  * Local Variables:
1064  * mode: C
1065  * tab-width: 8
1066  * indent-tabs-mode: t
1067  * c-file-style: "stroustrup"
1068  * End:
1069  * ex: shiftwidth=4 tabstop=8
1070  */
int rt_binunif_import5_minor_type(struct rt_db_internal *, const struct bu_external *, const mat_t, const struct db_i *, struct resource *, int)
Definition: db5_bin.c:113
Definition: db_flip.c:35
char * d_namep
pointer to name string
Definition: raytrace.h:859
Definition: raytrace.h:800
void bu_log(const char *,...) _BU_ATTR_PRINTF12
Definition: log.c:176
size_t db5_standardize_avs(struct bu_attribute_value_set *avs)
Definition: db5_attr.c:223
#define BU_AVS_MAGIC
Definition: magic.h:46
void db5_make_free_object_hdr(struct bu_external *ep, size_t length)
Definition: db5_io.c:572
#define BU_GLONGLONG(_cp)
Definition: cv.h:415
void bu_avs_init_empty(struct bu_attribute_value_set *avp)
Definition: avs.c:36
#define BU_EXTERNAL_INIT_ZERO
Definition: parse.h:239
size_t d_len
of db granules used
Definition: raytrace.h:867
void db5_export_attributes(struct bu_external *ap, const struct bu_attribute_value_set *avs)
Definition: attributes.c:153
#define RT_DIR_INMEM
object is in memory (only)
Definition: raytrace.h:889
const mat_t bn_mat_identity
Matrix and vector functionality.
Definition: mat.c:46
Definition: clone.c:90
const unsigned char * db5_get_raw_internal_ptr(struct db5_raw_internal *rip, const unsigned char *ip)
Definition: db5_io.c:245
#define BU_ASSERT_LONG(_lhs, _relation, _rhs)
Definition: defines.h:240
int rt_db_external5_to_internal5(struct rt_db_internal *ip, const struct bu_external *ep, const char *name, const struct db_i *dbip, const mat_t mat, struct resource *resp)
Definition: db5_io.c:854
void db5_make_free_object(struct bu_external *ep, size_t length)
Definition: db5_io.c:601
void bu_free_external(struct bu_external *ep)
void db5_export_object3(struct bu_external *out, int dli, const char *name, const unsigned char hidden, const struct bu_external *attrib, const struct bu_external *body, int major, int minor, int a_zzz, int b_zzz)
Definition: db5_io.c:420
Header file for the BRL-CAD common definitions.
int db5_put_color_table(struct db_i *dbip)
Definition: db5_io.c:1009
int db5_decode_signed(size_t *lenp, const unsigned char *cp, int format)
Definition: db5_io.c:119
int rt_db_get_internal5(struct rt_db_internal *ip, const struct directory *dp, const struct db_i *dbip, const mat_t mat, struct resource *resp)
Definition: db5_io.c:961
size_t db5_decode_length(size_t *lenp, const unsigned char *cp, int format)
Definition: db5_io.c:94
#define ID_BINUNIF
Uniform-array binary.
Definition: raytrace.h:501
void * bu_malloc(size_t siz, const char *str)
Definition: malloc.c:314
void rt_color_addrec(int low, int hi, int r, int g, int b, off_t addr)
Definition: mater.c:176
char * strchr(const char *sp, int c)
int idb_major_type
Definition: raytrace.h:192
void bu_vls_free(struct bu_vls *vp)
Definition: vls.c:248
int db5_realloc(struct db_i *dbip, struct directory *dp, struct bu_external *ep)
Definition: db5_alloc.c:96
#define BU_CK_VLS(_vp)
Definition: vls.h:69
struct resource rt_uniresource
default. Defined in librt/globals.c
Definition: globals.c:41
#define RT_G_DEBUG
Definition: raytrace.h:1718
#define RT_CK_DB_INTERNAL(_p)
Definition: raytrace.h:207
void db5_import_color_table(char *cp)
Definition: db5_io.c:996
int db_get_external(struct bu_external *ep, const struct directory *dp, const struct db_i *dbip)
void * bu_calloc(size_t nelem, size_t elsize, const char *str)
Definition: malloc.c:321
#define RT_CK_DIR(_dp)
Definition: raytrace.h:876
#define htonll(_val)
Definition: cv.h:59
#define RT_DB_INTERNAL_INIT(_p)
Definition: raytrace.h:199
const struct rt_functab * idb_meth
for ft_ifree(), etc.
Definition: raytrace.h:194
uint8_t * ext_buf
Definition: parse.h:216
int db5_select_length_encoding(size_t len)
Definition: db5_io.c:84
#define BU_GSHORT(_cp)
Definition: cv.h:439
#define RT_DIR_PHONY_ADDR
Special marker for d_addr field.
Definition: raytrace.h:879
void rt_vls_color_map(struct bu_vls *str)
Definition: mater.c:221
int db5_header_is_valid(const unsigned char *hp)
Definition: db5_io.c:46
struct bu_attribute_value_set idb_avs
Definition: raytrace.h:196
int db5_get_raw_internal_fp(struct db5_raw_internal *rip, FILE *fp)
Definition: db5_io.c:305
int(* ft_import5)(struct rt_db_internal *, const struct bu_external *, const mat_t, const struct db_i *, struct resource *)
Definition: raytrace.h:2131
uint32_t magic
Definition: avs.h:84
goto out
Definition: nmg_mod.c:3846
char * bu_vls_addr(const struct bu_vls *vp)
Definition: vls.c:111
int rt_db_cvt_to_external5(struct bu_external *ext, const char *name, const struct rt_db_internal *ip, double conv2mm, struct db_i *dbip, struct resource *resp, const int major)
Definition: db5_io.c:632
int db5_import_attributes(struct bu_attribute_value_set *avs, const struct bu_external *ap)
Definition: attributes.c:30
#define RT_CK_DBI(_p)
Definition: raytrace.h:829
int dbi_read_only
!0 => read only file
Definition: raytrace.h:806
union directory::@8 d_un
#define BU_GLONG(_cp)
Definition: cv.h:429
int db_wrap_v5_external(struct bu_external *ep, const char *name)
Definition: db5_io.c:702
#define BU_AVS_INIT(_ap)
Definition: avs.h:102
#define DEBUG_DB
5 Database debugging
Definition: raytrace.h:88
const struct rt_functab OBJ[]
Definition: table.c:159
#define RT_CK_RESOURCE(_p)
Definition: raytrace.h:1490
#define BU_ASSERT_PTR(_lhs, _relation, _rhs)
Definition: defines.h:227
int db5_crack_disk_header(struct db5_raw_internal *rip, const unsigned char *cp)
Definition: db5_io.c:182
int db5_get_attributes(const struct db_i *dbip, struct bu_attribute_value_set *avs, const struct directory *dp)
Definition: db5_io.c:1027
#define BU_EXTERNAL_INIT(_p)
Definition: parse.h:229
void * ptr
ptr to in-memory-only obj
Definition: raytrace.h:862
int db_put_external5(struct bu_external *ep, struct directory *dp, struct db_i *dbip)
Definition: db5_io.c:744
int idb_minor_type
ID_xxx.
Definition: raytrace.h:193
int dbi_version
PRIVATE: use db_version()
Definition: raytrace.h:824
#define BU_ASSERT_SIZE_T(_lhs, _relation, _rhs)
Definition: defines.h:253
unsigned char * db5_encode_length(unsigned char *cp, size_t val, int format)
Definition: db5_io.c:152
int db_write(struct db_i *dbip, const void *addr, size_t count, off_t offset)
Definition: db_io.c:170
#define BU_CK_EXTERNAL(_p)
Definition: parse.h:224
#define BU_VLS_INIT_ZERO
Definition: vls.h:84
size_t ext_nbytes
Definition: parse.h:210
int d_flags
flags
Definition: raytrace.h:869
Definition: vls.h:56
void bu_bomb(const char *str) _BU_ATTR_NORETURN
Definition: bomb.c:91
int rt_db_put_internal5(struct directory *dp, struct db_i *dbip, struct rt_db_internal *ip, struct resource *resp, const int major)
Definition: db5_io.c:789
char * dbi_filename
file name
Definition: raytrace.h:805
void db5_export_color_table(struct bu_vls *ostr, struct db_i *dbip)
Definition: db5_io.c:988
int(* ft_export5)(struct bu_external *, const struct rt_db_internal *, double, const struct db_i *, struct resource *)
Definition: raytrace.h:2138
void rt_db_free_internal(struct rt_db_internal *ip)
Definition: dir.c:216
int db5_update_attribute(const char *obj_name, const char *aname, const char *value, struct db_i *dbip)
Definition: attributes.c:365
#define BU_STR_EQUAL(s1, s2)
Definition: str.h:126