summaryrefslogtreecommitdiff
path: root/src/upb_mm.c
blob: f831671687e0975532d6e8d5a3fbfc16b27097eb (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
/*
 * upb - a minimalist implementation of protocol buffers.
 *
 * Copyright (c) 2009 Joshua Haberman.  See LICENSE for details.
 */

#include "upb_mm.h"
#include "upb_string.h"
#include "upb_array.h"
#include "upb_msg.h"

static void upb_mm_destroy(union upb_value_ptr p, upb_mm_ptrtype type)
{
  if(*p.msg) {
    union upb_mmptr mmptr = upb_mmptr_read(p, type);
    upb_mm_unref(mmptr, type);
  }
}

static union upb_mmptr upb_mm_newptr(upb_mm_ptrtype type,
                                     struct upb_fielddef *f)
{
  union upb_mmptr p = {NULL};
  switch(type) {
    case UPB_MM_MSG_REF: p.msg = upb_msg_new(upb_downcast_msgdef(f->def));
    case UPB_MM_STR_REF: p.str = upb_string_new();
    case UPB_MM_ARR_REF: p.arr = upb_array_new(f);
    default: assert(false); break;
  }
  return p;
}

static struct upb_mm_ref *find_or_create_ref(struct upb_mm_ref *fromref,
                                             struct upb_mm *mm,
                                             union upb_mmptr p, upb_mm_ptrtype type,
                                             bool *created)
{
  struct upb_mmhead *head = upb_mmhead_addr(p, type);
  struct upb_mm_ref **ref = &head->refs;
  while(*ref && (*ref)->mm <= mm) {
    if((*ref)->mm == mm) {
      *created = false;
      return *ref;
    }
    ref = &((*ref)->next);
  }
  *created = true;
  struct upb_mm_ref *newref = mm->newref_cb(fromref, p, type);
  newref->p = p;
  newref->type = type;
  newref->mm = mm;
  newref->next = *ref;
  *ref = newref;
  return newref;
}

struct upb_mm_ref *upb_mm_getref(union upb_mmptr p, upb_mm_ptrtype type,
                                 struct upb_mm *mm, bool *created)
{
  return find_or_create_ref(NULL, mm, p, type, created);
}

struct upb_mm_ref *upb_mm_newmsg_ref(struct upb_msgdef *def, struct upb_mm *mm)
{
  struct upb_msg *msg = upb_msg_new(def);
  union upb_mmptr mmptr = {.msg = msg};
  bool created;
  struct upb_mm_ref *ref = find_or_create_ref(NULL, mm, mmptr, UPB_MM_MSG_REF, &created);
  upb_mm_unref(mmptr, UPB_MM_MSG_REF);  /* Shouldn't have any counted refs. */
  assert(created);
  return ref;
}

struct upb_mm_ref *upb_mm_getfieldref(struct upb_mm_ref *msgref,
                                      struct upb_fielddef *f,
                                      bool *refcreated)
{
  assert(upb_field_ismm(f));
  upb_mm_ptrtype ptrtype = upb_field_ptrtype(f);
  struct upb_msg *msg = msgref->p.msg;
  union upb_mmptr val;
  union upb_value_ptr p = upb_msg_getptr(msg, f);

  /* Create the upb value if it doesn't already exist. */
  if(!upb_msg_isset(msg, f)) {
    upb_msg_set(msg, f);
    val = upb_mm_newptr(ptrtype, f);
    upb_mmptr_write(p, val, ptrtype);
  } else {
    val = upb_mmptr_read(p, ptrtype);
  }

  return find_or_create_ref(msgref, msgref->mm, val, ptrtype, refcreated);
}

struct upb_mm_ref *upb_mm_getelemref(struct upb_mm_ref *arrref, upb_arraylen_t i,
                                     bool *refcreated)
{
  struct upb_array *arr = arrref->p.arr;
  struct upb_fielddef *f = arr->fielddef;
  assert(upb_elem_ismm(f));
  assert(i < arr->len);
  union upb_value_ptr p = upb_array_getelementptr(arr, i);
  upb_mm_ptrtype type = upb_elem_ptrtype(f);
  union upb_mmptr val = upb_mmptr_read(p, type);
  return find_or_create_ref(arrref, arrref->mm, val, type, refcreated);
}

void upb_mm_release(struct upb_mm_ref *ref)
{
  struct upb_mm_ref **ref_head = (void*)ref->p.msg;
  struct upb_mm_ref **ref_elem = ref_head;
  struct upb_mm *mm = ref->mm;
  while(true) {
    assert(*ref_elem);  /* Client asserts r->mm is in the list. */
    if((*ref_elem)->mm == mm) {
      *ref_elem = (*ref_elem)->next;  /* Remove from the list. */
      break;
    }
  }

  if(upb_mmhead_norefs(&ref->p.msg->mmhead)) {
    /* Destroy the dynamic object. */
    switch(ref->type) {
      case UPB_MM_MSG_REF:
        upb_msg_destroy(ref->p.msg);
        break;
      case UPB_MM_ARR_REF:
        upb_array_destroy(ref->p.arr);
        break;
      case UPB_MM_STR_REF:
        upb_string_destroy(ref->p.str);
        break;
      default: assert(false); break;
    }
  }
}

void upb_mm_msg_set(struct upb_mm_ref *from_msg_ref, struct upb_mm_ref *to_ref,
                    struct upb_fielddef *f)
{
  assert(upb_field_ismm(f));
  union upb_mmptr fromval = from_msg_ref->p;
  union upb_mmptr toval = to_ref->p;
  union upb_value_ptr field_p = upb_msg_getptr(fromval.msg, f);
  upb_mm_ptrtype type = upb_field_ptrtype(f);
  if(upb_msg_isset(fromval.msg, f)) {
    union upb_mmptr existingval = upb_mmptr_read(field_p, type);
    if(existingval.msg == toval.msg)
      return;  /* Setting to its existing value, do nothing. */
    upb_mm_unref(existingval, type);
  }
  upb_msg_set(fromval.msg, f);
  upb_mmptr_write(field_p, toval, type);
  upb_mm_ref(toval, type);
}

void upb_mm_msgclear(struct upb_mm_ref *from_msg_ref, struct upb_fielddef *f)
{
  assert(upb_field_ismm(f));
  union upb_mmptr fromval = from_msg_ref->p;
  upb_mm_ptrtype type = upb_field_ptrtype(f);
  if(upb_msg_isset(fromval.msg, f)) {
    union upb_value_ptr field_p = upb_msg_getptr(fromval.msg, f);
    union upb_mmptr existingval = upb_mmptr_read(field_p, type);
    upb_msg_unset(fromval.msg, f);
    upb_mm_unref(existingval, type);
  }
}

void upb_mm_msgclear_all(struct upb_mm_ref *from)
{
  struct upb_msgdef *def = from->p.msg->def;
  for(upb_field_count_t i = 0; i < def->num_fields; i++) {
    struct upb_fielddef *f = &def->fields[i];
    if(!upb_field_ismm(f)) continue;
    upb_mm_msgclear(from, f);
  }
}

void upb_mm_arr_set(struct upb_mm_ref *from, struct upb_mm_ref *to,
                    upb_arraylen_t i, upb_field_type_t type)
{
  (void)from;
  (void)to;
  (void)i;
  (void)type;
}
generated by cgit on debian on lair
contact matthew@masot.net with questions or feedback