/* * prop_object_release -- * Decrement the reference count on an object. * * Free the object if we are releasing the final * reference. */ void prop_object_release(prop_object_t obj) { struct _prop_object *po; struct _prop_stack stack; void (*unlock)(void); int ret; uint32_t ocnt; _prop_stack_init(&stack); do { do { po = obj; _PROP_ASSERT(obj); if (po->po_type->pot_lock != NULL) po->po_type->pot_lock(); /* Save pointer to object unlock function */ unlock = po->po_type->pot_unlock; _PROP_ATOMIC_DEC32_NV(&po->po_refcnt, ocnt); ocnt++; _PROP_ASSERT(ocnt != 0); if (ocnt != 1) { ret = 0; if (unlock != NULL) unlock(); break; } ret = (po->po_type->pot_free)(&stack, &obj); if (unlock != NULL) unlock(); if (ret == _PROP_OBJECT_FREE_DONE) break; _PROP_ATOMIC_INC32(&po->po_refcnt); } while (ret == _PROP_OBJECT_FREE_RECURSE); if (ret == _PROP_OBJECT_FREE_FAILED) prop_object_release_emergency(obj); } while (_prop_stack_pop(&stack, &obj, NULL, NULL, NULL)); }
static void _prop_array_emergency_free(prop_object_t obj) { prop_array_t pa = obj; _PROP_ASSERT(pa->pa_count != 0); --pa->pa_count; }
static _prop_object_free_rv_t _prop_array_free(prop_stack_t stack, prop_object_t *obj) { prop_array_t pa = *obj; prop_object_t po; _PROP_ASSERT(pa->pa_count <= pa->pa_capacity); _PROP_ASSERT((pa->pa_capacity == 0 && pa->pa_array == NULL) || (pa->pa_capacity != 0 && pa->pa_array != NULL)); /* The easy case is an empty array, just free and return. */ if (pa->pa_count == 0) { if (pa->pa_array != NULL) _PROP_FREE(pa->pa_array, M_PROP_ARRAY); _PROP_RWLOCK_DESTROY(pa->pa_rwlock); _PROP_POOL_PUT(_prop_array_pool, pa); return (_PROP_OBJECT_FREE_DONE); } if (pa->pa_array == NULL) return _PROP_OBJECT_FREE_DONE; po = pa->pa_array[pa->pa_count - 1]; _PROP_ASSERT(po != NULL); if (stack == NULL) { /* * If we are in emergency release mode, * just let caller recurse down. */ *obj = po; return (_PROP_OBJECT_FREE_FAILED); } /* Otherwise, try to push the current object on the stack. */ if (!_prop_stack_push(stack, pa, NULL, NULL, NULL)) { /* Push failed, entering emergency release mode. */ return (_PROP_OBJECT_FREE_FAILED); } /* Object pushed on stack, caller will release it. */ --pa->pa_count; *obj = po; return (_PROP_OBJECT_FREE_RECURSE); }
/* * prop_object_retain -- * Increment the reference count on an object. */ void prop_object_retain(prop_object_t obj) { struct _prop_object *po = obj; uint32_t ncnt; ncnt = atomic_inc_32_nv(&po->po_refcnt); _PROP_ASSERT(ncnt != 0); }
bool prop_object_equals_with_error(prop_object_t obj1, prop_object_t obj2, bool *error_flag) { struct _prop_object *po1; struct _prop_object *po2; void *stored_pointer1, *stored_pointer2; prop_object_t next_obj1, next_obj2; struct _prop_stack stack; _prop_object_equals_rv_t ret; _prop_stack_init(&stack); if (error_flag) *error_flag = false; start_subtree: stored_pointer1 = NULL; stored_pointer2 = NULL; po1 = obj1; po2 = obj2; if (po1->po_type != po2->po_type) return (false); continue_subtree: ret = (*po1->po_type->pot_equals)(obj1, obj2, &stored_pointer1, &stored_pointer2, &next_obj1, &next_obj2); if (ret == _PROP_OBJECT_EQUALS_FALSE) goto finish; if (ret == _PROP_OBJECT_EQUALS_TRUE) { if (!_prop_stack_pop(&stack, &obj1, &obj2, &stored_pointer1, &stored_pointer2)) return true; po1 = obj1; po2 = obj2; goto continue_subtree; } _PROP_ASSERT(ret == _PROP_OBJECT_EQUALS_RECURSE); if (!_prop_stack_push(&stack, obj1, obj2, stored_pointer1, stored_pointer2)) { if (error_flag) *error_flag = true; goto finish; } obj1 = next_obj1; obj2 = next_obj2; goto start_subtree; finish: while (_prop_stack_pop(&stack, &obj1, &obj2, NULL, NULL)) { po1 = obj1; (*po1->po_type->pot_equals_finish)(obj1, obj2); } return (false); }
/* * prop_object_retain -- * Increment the reference count on an object. */ void prop_object_retain(prop_object_t obj) { struct _prop_object *po = obj; uint32_t ncnt __unused; _PROP_ATOMIC_INC32_NV(&po->po_refcnt, ncnt); _PROP_ASSERT(ncnt != 0); }
static void _prop_array_iterator_reset_locked(void *v) { struct _prop_array_iterator *pai = v; prop_array_t pa = pai->pai_base.pi_obj; _PROP_ASSERT(prop_object_is_array(pa)); pai->pai_index = 0; pai->pai_base.pi_version = pa->pa_version; }
static void _prop_array_iterator_reset(void *v) { struct _prop_array_iterator *pai = v; prop_array_t pa = pai->pai_base.pi_obj; _PROP_ASSERT(prop_object_is_array(pa)); _PROP_RWLOCK_RDLOCK(pa->pa_rwlock); _prop_array_iterator_reset_locked(pai); _PROP_RWLOCK_UNLOCK(pa->pa_rwlock); }
/* * prop_object_retain -- * Increment the reference count on an object. */ void prop_object_retain(prop_object_t obj) { struct _prop_object *po = obj; uint32_t ocnt; _PROP_REFCNT_LOCK(); ocnt = po->po_refcnt++; _PROP_REFCNT_UNLOCK(); _PROP_ASSERT(ocnt != 0xffffffffU); }
static bool _prop_array_externalize(struct _prop_object_externalize_context *ctx, void *v) { prop_array_t pa = v; struct _prop_object *po; prop_object_iterator_t pi; unsigned int i; bool rv = false; _PROP_RWLOCK_RDLOCK(pa->pa_rwlock); if (pa->pa_count == 0) { _PROP_RWLOCK_UNLOCK(pa->pa_rwlock); return (_prop_object_externalize_empty_tag(ctx, "array")); } /* XXXJRT Hint "count" for the internalize step? */ if (_prop_object_externalize_start_tag(ctx, "array") == false || _prop_object_externalize_append_char(ctx, '\n') == false) goto out; pi = _prop_array_iterator_locked(pa); if (pi == NULL) goto out; ctx->poec_depth++; _PROP_ASSERT(ctx->poec_depth != 0); while ((po = _prop_array_iterator_next_object_locked(pi)) != NULL) { if ((*po->po_type->pot_extern)(ctx, po) == false) { prop_object_iterator_release(pi); goto out; } } prop_object_iterator_release(pi); ctx->poec_depth--; for (i = 0; i < ctx->poec_depth; i++) { if (_prop_object_externalize_append_char(ctx, '\t') == false) goto out; } if (_prop_object_externalize_end_tag(ctx, "array") == false) goto out; rv = true; out: _PROP_RWLOCK_UNLOCK(pa->pa_rwlock); return (rv); }
/* * prop_object_release -- * Decrement the reference count on an object. * * Free the object if we are releasing the final * reference. */ void prop_object_release(prop_object_t obj) { struct _prop_object *po; struct _prop_stack stack; int ret; uint32_t ocnt; _prop_stack_init(&stack); do { do { po = obj; _PROP_ASSERT(obj); _PROP_REFCNT_LOCK(); ocnt = po->po_refcnt--; _PROP_REFCNT_UNLOCK(); _PROP_ASSERT(ocnt != 0); if (ocnt != 1) { ret = 0; break; } ret = (po->po_type->pot_free)(&stack, &obj); if (ret == _PROP_OBJECT_FREE_DONE) break; _PROP_REFCNT_LOCK(); ++po->po_refcnt; _PROP_REFCNT_UNLOCK(); } while (ret == _PROP_OBJECT_FREE_RECURSE); if (ret == _PROP_OBJECT_FREE_FAILED) prop_object_release_emergency(obj); } while (_prop_stack_pop(&stack, &obj, NULL, NULL, NULL)); }
/* * _prop_object_externalize_append_char -- * Append a single character to the externalize buffer. */ bool _prop_object_externalize_append_char( struct _prop_object_externalize_context *ctx, unsigned char c) { _PROP_ASSERT(ctx->poec_capacity != 0); _PROP_ASSERT(ctx->poec_buf != NULL); _PROP_ASSERT(ctx->poec_len <= ctx->poec_capacity); if (ctx->poec_len == ctx->poec_capacity) { char *cp = _PROP_REALLOC(ctx->poec_buf, ctx->poec_capacity + BUF_EXPAND, M_TEMP); if (cp == NULL) return (false); ctx->poec_capacity = ctx->poec_capacity + BUF_EXPAND; ctx->poec_buf = cp; } ctx->poec_buf[ctx->poec_len++] = c; return (true); }
static prop_object_t _prop_array_iterator_next_object_locked(void *v) { struct _prop_array_iterator *pai = v; prop_array_t pa = pai->pai_base.pi_obj; prop_object_t po = NULL; _PROP_ASSERT(prop_object_is_array(pa)); if (pa->pa_version != pai->pai_base.pi_version) goto out; /* array changed during iteration */ _PROP_ASSERT(pai->pai_index <= pa->pa_count); if (pai->pai_index == pa->pa_count) goto out; /* we've iterated all objects */ po = pa->pa_array[pai->pai_index]; pai->pai_index++; out: return (po); }
static prop_object_t _prop_array_iterator_next_object(void *v) { struct _prop_array_iterator *pai = v; prop_array_t pa = pai->pai_base.pi_obj; prop_object_t po; _PROP_ASSERT(prop_object_is_array(pa)); _PROP_RWLOCK_RDLOCK(pa->pa_rwlock); po = _prop_array_iterator_next_object_locked(pai); _PROP_RWLOCK_UNLOCK(pa->pa_rwlock); return (po); }
/* * prop_array_set -- * Store a reference to an object at the specified array index. * This method is not allowed to create holes in the array; the * caller must either be setting the object just beyond the existing * count or replacing an already existing object reference. */ bool prop_array_set(prop_array_t pa, unsigned int idx, prop_object_t po) { prop_object_t opo; bool rv = false; if (! prop_object_is_array(pa)) return (false); _PROP_RWLOCK_WRLOCK(pa->pa_rwlock); if (prop_array_is_immutable(pa)) goto out; if (idx == pa->pa_count) { rv = _prop_array_add(pa, po); goto out; } _PROP_ASSERT(idx < pa->pa_count); opo = pa->pa_array[idx]; _PROP_ASSERT(opo != NULL); prop_object_retain(po); pa->pa_array[idx] = po; pa->pa_version++; prop_object_release(opo); rv = true; out: _PROP_RWLOCK_UNLOCK(pa->pa_rwlock); return (rv); }
/* * prop_array_get -- * Return the object stored at the specified array index. */ prop_object_t prop_array_get(prop_array_t pa, unsigned int idx) { prop_object_t po = NULL; if (! prop_object_is_array(pa)) return (NULL); _PROP_RWLOCK_RDLOCK(pa->pa_rwlock); if (idx >= pa->pa_count) goto out; po = pa->pa_array[idx]; _PROP_ASSERT(po != NULL); out: _PROP_RWLOCK_UNLOCK(pa->pa_rwlock); return (po); }
static bool _prop_array_add(prop_array_t pa, prop_object_t po) { /* * Array must be WRITE-LOCKED. */ _PROP_ASSERT(pa->pa_count <= pa->pa_capacity); if (prop_array_is_immutable(pa) || (pa->pa_count == pa->pa_capacity && _prop_array_expand(pa, pa->pa_capacity + EXPAND_STEP) == false)) return (false); prop_object_retain(po); pa->pa_array[pa->pa_count++] = po; pa->pa_version++; return (true); }
/* * _prop_object_internalize_decode_string -- * Decode an encoded string. */ bool _prop_object_internalize_decode_string( struct _prop_object_internalize_context *ctx, char *target, size_t targsize, size_t *sizep, const char **cpp) { const char *src; size_t tarindex; char c; tarindex = 0; src = ctx->poic_cp; for (;;) { if (_PROP_EOF(*src)) return (false); if (*src == '<') { break; } if ((c = *src) == '&') { if (src[1] == 'a' && src[2] == 'm' && src[3] == 'p' && src[4] == ';') { c = '&'; src += 5; } else if (src[1] == 'l' && src[2] == 't' && src[3] == ';') { c = '<'; src += 4; } else if (src[1] == 'g' && src[2] == 't' && src[3] == ';') { c = '>'; src += 4; } else if (src[1] == 'a' && src[2] == 'p' && src[3] == 'o' && src[4] == 's' && src[5] == ';') { c = '\''; src += 6; } else if (src[1] == 'q' && src[2] == 'u' && src[3] == 'o' && src[4] == 't' && src[5] == ';') { c = '\"'; src += 6; } else return (false); } else src++; if (target) { if (tarindex >= targsize) return (false); target[tarindex] = c; } tarindex++; } _PROP_ASSERT(*src == '<'); if (sizep != NULL) *sizep = tarindex; if (cpp != NULL) *cpp = src; return (true); }
/* * _prop_object_internalize_find_tag -- * Find the next tag in an XML stream. Optionally compare the found * tag to an expected tag name. State of the context is undefined * if this routine returns false. Upon success, the context points * to the first octet after the tag. */ bool _prop_object_internalize_find_tag(struct _prop_object_internalize_context *ctx, const char *tag, _prop_tag_type_t type) { const char *cp; size_t taglen; if (tag != NULL) taglen = strlen(tag); else taglen = 0; start_over: cp = ctx->poic_cp; /* * Find the start of the tag. */ while (_PROP_ISSPACE(*cp)) cp++; if (_PROP_EOF(*cp)) return (false); if (*cp != '<') return (false); ctx->poic_tag_start = cp++; if (_PROP_EOF(*cp)) return (false); if (*cp == '!') { if (cp[1] != '-' || cp[2] != '-') return (false); /* * Comment block -- only allowed if we are allowed to * return a start tag. */ if (type == _PROP_TAG_TYPE_END) return (false); ctx->poic_cp = cp + 3; if (_prop_object_internalize_skip_comment(ctx) == false) return (false); goto start_over; } if (*cp == '/') { if (type != _PROP_TAG_TYPE_END && type != _PROP_TAG_TYPE_EITHER) return (false); cp++; if (_PROP_EOF(*cp)) return (false); ctx->poic_tag_type = _PROP_TAG_TYPE_END; } else { if (type != _PROP_TAG_TYPE_START && type != _PROP_TAG_TYPE_EITHER) return (false); ctx->poic_tag_type = _PROP_TAG_TYPE_START; } ctx->poic_tagname = cp; while (!_PROP_ISSPACE(*cp) && *cp != '/' && *cp != '>') cp++; if (_PROP_EOF(*cp)) return (false); ctx->poic_tagname_len = cp - ctx->poic_tagname; /* Make sure this is the tag we're looking for. */ if (tag != NULL && (taglen != ctx->poic_tagname_len || memcmp(tag, ctx->poic_tagname, taglen) != 0)) return (false); /* Check for empty tag. */ if (*cp == '/') { if (ctx->poic_tag_type != _PROP_TAG_TYPE_START) return(false); /* only valid on start tags */ ctx->poic_is_empty_element = true; cp++; if (_PROP_EOF(*cp) || *cp != '>') return (false); } else ctx->poic_is_empty_element = false; /* Easy case of no arguments. */ if (*cp == '>') { ctx->poic_tagattr = NULL; ctx->poic_tagattr_len = 0; ctx->poic_tagattrval = NULL; ctx->poic_tagattrval_len = 0; ctx->poic_cp = cp + 1; return (true); } _PROP_ASSERT(!_PROP_EOF(*cp)); cp++; if (_PROP_EOF(*cp)) return (false); while (_PROP_ISSPACE(*cp)) cp++; if (_PROP_EOF(*cp)) return (false); ctx->poic_tagattr = cp; while (!_PROP_ISSPACE(*cp) && *cp != '=') cp++; if (_PROP_EOF(*cp)) return (false); ctx->poic_tagattr_len = cp - ctx->poic_tagattr; cp++; if (*cp != '\"') return (false); cp++; if (_PROP_EOF(*cp)) return (false); ctx->poic_tagattrval = cp; while (*cp != '\"') cp++; if (_PROP_EOF(*cp)) return (false); ctx->poic_tagattrval_len = cp - ctx->poic_tagattrval; cp++; if (*cp != '>') return (false); ctx->poic_cp = cp + 1; return (true); }