/* * Copyright © 2007 Pippijn van Steenhoven / The Ermyth Team * Rights to this code are as documented in doc/pod/license.pod. * * JSON-RPC for Ermyth * * $Id: value.C,v 1.5 2007/09/22 14:27:30 pippijn Exp $ */ //>>>>>>>>>>> value.C <<<<<<<<<< #include #include #include #include #include // size_t #include "atheme.h" #include #include namespace json { namespace impl { template struct equals { bool operator () (floattype A, floattype B) { inttype aInt = *(inttype*)&A; inttype bInt = *(inttype*)&B; // Make aInt lexicographically ordered as a twos-complement int if (aInt < 0) aInt = 0x80000000 - aInt; // Make bInt lexicographically ordered as a twos-complement int if (bInt < 0) bInt = 0x80000000 - bInt; inttype intDiff = abs(aInt - bInt); if (intDiff <= maxUlps) return true; return false; } }; } void unreachable_internal (char const * const file, int const line, char const * const function) __attribute__ ((__noreturn__)); void unreachable_internal (char const * const file, int const line, char const * const function) { char buf[1024]; snprintf (buf, 1024, "%s (%d) [%s] critical: Unreachable line reached.", file, line, function); throw std::runtime_error (buf); } void throw_unless_internal (char const * const file, int const line, char const * const function, char const * const condition) __attribute__ ((__noreturn__)); void throw_unless_internal (char const * const file, int const line, char const * const function, char const * const condition) { char buf[1024]; snprintf (buf, 1024, "%s (%d) [%s] critical: Assertion `%s' failed.", file, line, function, condition); throw std::runtime_error (buf); } void throw_msg_unless_internal (char const * const file, int const line, char const * const function, char const * const message) __attribute__ ((__noreturn__)); void throw_msg_unless_internal (char const * const file, int const line, char const * const function, char const * const message) { char buf[1024]; snprintf (buf, 1024, "%s (%d) [%s] critical: %s.", file, line, function, message); throw std::runtime_error (buf); } #define throw_unreachable unreachable_internal (__FILE__, __LINE__, __PRETTY_FUNCTION__) #define throw_unless(condition) if (!expect_false (condition)) throw_unless_internal (__FILE__, __LINE__, __PRETTY_FUNCTION__, #condition) #define throw_msg_unless(condition, message) if (!expect_false (condition)) throw_msg_unless_internal (__FILE__, __LINE__, __PRETTY_FUNCTION__, message) const Value Value::null; const int Value::minInt = int (~ (unsigned (-1)/2)); const int Value::maxInt = int (unsigned (-1)/2); const unsigned Value::maxUInt = unsigned (-1); ValueAllocator::~ValueAllocator () { } class DefaultValueAllocator : public ValueAllocator { public: virtual ~DefaultValueAllocator () { } virtual char *makeMemberName (char const *memberName) { return duplicateStringValue (memberName); } virtual void releaseMemberName (char *memberName) { releaseStringValue (memberName); } virtual char *duplicateStringValue (char const *value, unsigned length = unknown) { //@todo invesgate this old optimization #if 0 if (!value || value[0] == 0) return 0; #endif if (length == unknown) length = (unsigned)strlen (value); char *newString = salloc (length + 1); memcpy (newString, value, length); newString[length] = 0; return newString; } virtual void releaseStringValue (char *value) { if (value) sfree (value); } }; static ValueAllocator *&valueAllocator () { static DefaultValueAllocator defaultAllocator; static ValueAllocator *valueAllocator = &defaultAllocator; return valueAllocator; } static struct DummyValueAllocatorInitializer { DummyValueAllocatorInitializer () { valueAllocator (); // ensure valueAllocator () statics are initialized before main (). } } dummyValueAllocatorInitializer; // class Value::CommentInfo Value::CommentInfo::CommentInfo () : comment_ (0) { } Value::CommentInfo::~CommentInfo () { if (comment_) valueAllocator()->releaseStringValue (comment_); } void Value::CommentInfo::setComment (char const *text) { if (comment_) valueAllocator()->releaseStringValue (comment_); throw_unless (text); throw_msg_unless (text[0]=='\0' || text[0]=='/', "Comments must start with /"); // It seems that /**/ style comments are acceptable as well. comment_ = valueAllocator()->duplicateStringValue (text); } // class Value::CZString // Notes: index_ indicates if the string was allocated when // a string is stored. Value::CZString::CZString (int idx) : cstr_ (0) , index_ (idx) { } Value::CZString::CZString (char const *cstr, DuplicationPolicy allocate) : cstr_ (allocate == duplicate ? valueAllocator()->makeMemberName (cstr) : cstr) , index_ (allocate) { } Value::CZString::CZString (const CZString &other) : cstr_ (other.index_ != noDuplication && other.cstr_ != 0 ? valueAllocator()->makeMemberName (other.cstr_) : other.cstr_) , index_ (other.cstr_ ? (other.index_ == noDuplication ? noDuplication : duplicate) : other.index_) { } Value::CZString::~CZString () { if (cstr_ && index_ == duplicate) valueAllocator()->releaseMemberName (const_cast (cstr_)); } void Value::CZString::swap (CZString &other) { std::swap (cstr_, other.cstr_); std::swap (index_, other.index_); } Value::CZString & Value::CZString::operator = (const CZString &other) { CZString temp (other); swap (temp); return *this; } bool Value::CZString::operator < (const CZString &other) const { if (cstr_) return strcmp (cstr_, other.cstr_) < 0; return index_ < other.index_; } bool Value::CZString::operator == (const CZString &other) const { if (cstr_) return strcmp (cstr_, other.cstr_) == 0; return index_ == other.index_; } int Value::CZString::index () const { return index_; } char const * Value::CZString::c_str () const { return cstr_; } bool Value::CZString::isStaticString () const { return index_ == noDuplication; } // class Value::Value /*! \internal Default constructor initialization must be equivalent to: * memset (this, 0, sizeof (Value)) * This optimization is used in ValueInternalMap fast allocator. */ Value::Value (ValueType t) : type_ (t) , allocated_ (0) , comments_ (0) { switch (t) { case nullValue: break; case intValue: case uintValue: value_.int_ = 0; break; case realValue: value_.real_ = 0.0; break; case stringValue: value_.string_ = 0; break; case arrayValue: case objectValue: value_.map_ = new ObjectValues (); break; case booleanValue: value_.bool_ = false; break; default: throw_unreachable; } } Value::Value (int value) : type_ (intValue) , comments_ (0) { value_.int_ = value; } Value::Value (unsigned value) : type_ (uintValue) , comments_ (0) { value_.uint_ = value; } Value::Value (double value) : type_ (realValue) , comments_ (0) { value_.real_ = value; } Value::Value (char const *value) : type_ (stringValue) , allocated_ (true) , comments_ (0) { value_.string_ = valueAllocator()->duplicateStringValue (value); } Value::Value (const std::string &value) : type_ (stringValue) , allocated_ (true) , comments_ (0) { value_.string_ = valueAllocator()->duplicateStringValue (value.c_str (), (unsigned)value.length ()); } Value::Value (const StaticString &value) : type_ (stringValue) , allocated_ (false) , comments_ (0) { value_.string_ = const_cast (value.c_str ()); } Value::Value (bool value) : type_ (booleanValue) , comments_ (0) { value_.bool_ = value; } Value::Value (const Value &other) : type_ (other.type_) , comments_ (0) { switch (type_) { case nullValue: case intValue: case uintValue: case realValue: case booleanValue: value_ = other.value_; break; case stringValue: if (other.value_.string_) { value_.string_ = valueAllocator()->duplicateStringValue (other.value_.string_); allocated_ = true; } else value_.string_ = 0; break; case arrayValue: case objectValue: value_.map_ = new ObjectValues (*other.value_.map_); break; default: throw_unreachable; } if (other.comments_) { comments_ = new CommentInfo[numberOfCommentPlacement]; for (int comment = 0; comment < numberOfCommentPlacement; ++comment) { const CommentInfo &otherComment = other.comments_[comment]; if (otherComment.comment_) comments_[comment].setComment (otherComment.comment_); } } } Value::~Value () { switch (type_) { case nullValue: case intValue: case uintValue: case realValue: case booleanValue: break; case stringValue: if (allocated_) valueAllocator()->releaseStringValue (value_.string_); break; case arrayValue: case objectValue: delete value_.map_; break; default: throw_unreachable; } if (comments_) delete[] comments_; } Value & Value::operator = (const Value &other) { Value temp (other); swap (temp); return *this; } void Value::swap (Value &other) { ValueType temp = type_; type_ = other.type_; other.type_ = temp; std::swap (value_, other.value_); int temp2 = allocated_; allocated_ = other.allocated_; other.allocated_ = temp2; } ValueType Value::type () const { return type_; } bool Value::operator < (const Value &other) const { int typeDelta = type_ - other.type_; if (typeDelta) return typeDelta < 0 ? true : false; switch (type_) { case nullValue: return false; case intValue: return value_.int_ < other.value_.int_; case uintValue: return value_.uint_ < other.value_.uint_; case realValue: return value_.real_ < other.value_.real_; case booleanValue: return value_.bool_ < other.value_.bool_; case stringValue: return (value_.string_ == 0 && other.value_.string_) || (other.value_.string_ && value_.string_ && strcmp (value_.string_, other.value_.string_) < 0); case arrayValue: case objectValue: { int delta = int (value_.map_->size () - other.value_.map_->size ()); if (delta) return delta < 0; return (*value_.map_) < (*other.value_.map_); } default: throw_unreachable; } return 0; // unreachable } bool Value::operator <= (const Value &other) const { return !(other > *this); } bool Value::operator >= (const Value &other) const { return !(*this < other); } bool Value::operator > (const Value &other) const { return other < *this; } bool Value::operator == (const Value &other) const { if (type_ != other.type_) return false; switch (type_) { case nullValue: return true; case intValue: return value_.int_ == other.value_.int_; case uintValue: return value_.uint_ == other.value_.uint_; case realValue: return impl::equals<> () (value_.real_, other.value_.real_); case booleanValue: return value_.bool_ == other.value_.bool_; case stringValue: return (value_.string_ == other.value_.string_) || (other.value_.string_ && value_.string_ && strcmp (value_.string_, other.value_.string_) == 0); case arrayValue: case objectValue: return value_.map_->size () == other.value_.map_->size () && (*value_.map_) == (*other.value_.map_); default: throw_unreachable; } return 0; // unreachable } bool Value::operator != (const Value &other) const { return !(*this == other); } Value::operator char const * () const { throw_unless (type_ == stringValue); return value_.string_; } Value::operator std::string () const { switch (type_) { case nullValue: return ""; case stringValue: return value_.string_ ? value_.string_ : ""; case booleanValue: return value_.bool_ ? "true" : "false"; case intValue: case uintValue: case realValue: case arrayValue: case objectValue: throw_msg_unless (false, "Type is not convertible to string"); default: throw_unreachable; } return ""; // unreachable } Value::operator int () const { switch (type_) { case nullValue: return 0; case intValue: return value_.int_; case uintValue: throw_msg_unless (value_.uint_ < (unsigned)maxInt, "integer out of signed integer range"); return value_.uint_; case realValue: throw_msg_unless (value_.real_ >= minInt && value_.real_ <= maxInt, "Real out of signed integer range"); return int (value_.real_); case booleanValue: return value_.bool_ ? 1 : 0; case stringValue: case arrayValue: case objectValue: throw_msg_unless (false, "Type is not convertible to int"); default: throw_unreachable; } return 0; // unreachable; } Value::operator unsigned () const { switch (type_) { case nullValue: return 0; case intValue: throw_msg_unless (value_.int_ >= 0, "Negative integer can not be converted to unsigned integer"); return value_.int_; case uintValue: return value_.uint_; case realValue: throw_msg_unless (value_.real_ >= 0 && value_.real_ <= maxUInt, "Real out of unsigned integer range"); return unsigned (value_.real_); case booleanValue: return value_.bool_ ? 1 : 0; case stringValue: case arrayValue: case objectValue: throw_msg_unless (false, "Type is not convertible to uint"); default: throw_unreachable; } return 0; // unreachable; } Value::operator double () const { switch (type_) { case nullValue: return 0.0; case intValue: return value_.int_; case uintValue: return value_.uint_; case realValue: return value_.real_; case booleanValue: return value_.bool_ ? 1.0 : 0.0; case stringValue: case arrayValue: case objectValue: throw_msg_unless (false, "Type is not convertible to double"); default: throw_unreachable; } return 0; // unreachable; } Value::operator bool () const { switch (type_) { case nullValue: return false; case intValue: case uintValue: return value_.int_ != 0; case realValue: return impl::equals<> () (value_.real_, 0.0); case booleanValue: return value_.bool_; case stringValue: return value_.string_ && value_.string_[0] != 0; case arrayValue: case objectValue: return value_.map_->size () != 0; default: throw_unreachable; } return false; // unreachable; } bool Value::isConvertibleTo (ValueType other) const { switch (type_) { case nullValue: return true; case intValue: return (other == nullValue && value_.int_ == 0) || other == intValue || (other == uintValue && value_.int_ >= 0) || other == realValue || other == stringValue || other == booleanValue; case uintValue: return (other == nullValue && value_.uint_ == 0) || (other == intValue && value_.uint_ <= (unsigned)maxInt) || other == uintValue || other == realValue || other == stringValue || other == booleanValue; case realValue: return (other == nullValue && impl::equals<> () (value_.real_, 0.0)) || (other == intValue && value_.real_ >= minInt && value_.real_ <= maxInt) || (other == uintValue && value_.real_ >= 0 && value_.real_ <= maxUInt) || other == realValue || other == stringValue || other == booleanValue; case booleanValue: return (other == nullValue && value_.bool_ == false) || other == intValue || other == uintValue || other == realValue || other == stringValue || other == booleanValue; case stringValue: return other == stringValue || (other == nullValue && (!value_.string_ || value_.string_[0] == 0)); case arrayValue: return other == arrayValue || (other == nullValue && value_.map_->size () == 0); case objectValue: return other == objectValue || (other == nullValue && value_.map_->size () == 0); default: throw_unreachable; } return false; // unreachable; } /// Number of values in array or object unsigned Value::size () const { switch (type_) { case nullValue: case intValue: case uintValue: case realValue: case booleanValue: case stringValue: return 0; case arrayValue: // size of the array is highest index + 1 if (!value_.map_->empty ()) { ObjectValues::const_iterator itLast = value_.map_->end (); --itLast; return itLast->first.index ()+1; } return 0; case objectValue: return int (value_.map_->size ()); default: throw_unreachable; } return 0; // unreachable; } bool Value::empty () const { if (isNull () || isArray () || isObject ()) return size () == 0u; else return false; } bool Value::operator ! () const { return isNull (); } void Value::clear () { throw_unless (type_ == nullValue || type_ == arrayValue || type_ == objectValue); switch (type_) { case arrayValue: case objectValue: value_.map_->clear (); break; default: break; } } void Value::resize (unsigned newSize) { throw_unless (type_ == nullValue || type_ == arrayValue); if (type_ == nullValue) *this = Value (arrayValue); unsigned oldSize = size (); if (newSize == 0) clear (); else if (newSize > oldSize) (*this)[ newSize - 1 ]; else { for (unsigned index = newSize; index < oldSize; ++index) value_.map_->erase (index); throw_unless (size () == newSize); } } Value & Value::operator [] (int index) { return operator [] (static_cast (index)); } Value & Value::operator [] (unsigned index) { throw_unless (type_ == nullValue || type_ == arrayValue); if (type_ == nullValue) *this = Value (arrayValue); CZString key (index); ObjectValues::iterator it = value_.map_->lower_bound (key); if (it != value_.map_->end () && it->first == key) return it->second; ObjectValues::value_type defaultValue (key, null); it = value_.map_->insert (it, defaultValue); return it->second; } const Value & Value::operator [] (int index) const { return operator [] (static_cast (index)); } const Value & Value::operator [] (unsigned index) const { throw_unless (type_ == nullValue || type_ == arrayValue); if (type_ == nullValue) return null; CZString key (index); ObjectValues::const_iterator it = value_.map_->find (key); if (it == value_.map_->end ()) return null; return it->second; } Value & Value::operator [] (char const *key) { return resolveReference (key, false); } Value & Value::resolveReference (char const *key, bool isStatic) { throw_unless (type_ == nullValue || type_ == objectValue); if (type_ == nullValue) *this = Value (objectValue); CZString actualKey (key, isStatic ? CZString::noDuplication : CZString::duplicateOnCopy); ObjectValues::iterator it = value_.map_->lower_bound (actualKey); if (it != value_.map_->end () && it->first == actualKey) return it->second; ObjectValues::value_type defaultValue (actualKey, null); it = value_.map_->insert (it, defaultValue); Value &value = it->second; return value; } Value Value::get (int index, const Value &defaultValue) const { return get (static_cast (index), defaultValue); } Value Value::get (unsigned index, const Value &defaultValue) const { const Value *value = &((*this)[index]); return value == &null ? defaultValue : *value; } bool Value::isValidIndex (int index) const { return isValidIndex (static_cast (index)); } bool Value::isValidIndex (unsigned index) const { return index < size (); } const Value & Value::operator [] (char const *key) const { throw_unless (type_ == nullValue || type_ == objectValue); if (type_ == nullValue) return null; CZString actualKey (key, CZString::noDuplication); ObjectValues::const_iterator it = value_.map_->find (actualKey); if (it == value_.map_->end ()) return null; return it->second; } Value & Value::operator [] (const std::string &key) { return (*this)[ key.c_str () ]; } const Value & Value::operator [] (const std::string &key) const { return (*this)[ key.c_str () ]; } Value & Value::operator [] (const StaticString &key) { return resolveReference (key, true); } Value & Value::append (const Value &value) { return (*this)[size ()] = value; } Value Value::get (char const *key, const Value &defaultValue) const { const Value *value = &((*this)[key]); return value == &null ? defaultValue : *value; } Value Value::get (const std::string &key, const Value &defaultValue) const { return get (key.c_str (), defaultValue); } Value Value::removeMember (char const *key) { throw_unless (type_ == nullValue || type_ == objectValue); if (type_ == nullValue) return null; CZString actualKey (key, CZString::noDuplication); ObjectValues::iterator it = value_.map_->find (actualKey); if (it == value_.map_->end ()) return null; Value old (it->second); value_.map_->erase (it); return old; } Value Value::removeMember (const std::string &key) { return removeMember (key.c_str ()); } bool Value::isMember (char const *key) const { const Value *value = &((*this)[key]); return value != &null; } bool Value::isMember (const std::string &key) const { return isMember (key.c_str ()); } Value::Members Value::getMemberNames () const { throw_unless (type_ == nullValue || type_ == objectValue); if (type_ == nullValue) return Value::Members (); Members members; members.reserve (value_.map_->size ()); ObjectValues::const_iterator it; ObjectValues::const_iterator itEnd = value_.map_->end (); for (it = value_.map_->begin (); it != itEnd; ++it) members.push_back (std::string (it->first.c_str())); return members; } bool Value::isNull () const { return type_ == nullValue; } bool Value::isBool () const { return type_ == booleanValue; } bool Value::isInt () const { return type_ == intValue; } bool Value::isUInt () const { return type_ == uintValue; } bool Value::isIntegral () const { return type_ == intValue || type_ == uintValue || type_ == booleanValue; } bool Value::isDouble () const { return type_ == realValue; } bool Value::isNumeric () const { return isIntegral () || isDouble (); } bool Value::isString () const { return type_ == stringValue; } bool Value::isArray () const { return type_ == nullValue || type_ == arrayValue; } bool Value::isObject () const { return type_ == nullValue || type_ == objectValue; } void Value::setComment (char const *comment, CommentPlacement placement) { if (!comments_) comments_ = new CommentInfo[numberOfCommentPlacement]; comments_[placement].setComment (comment); } void Value::setComment (const std::string &comment, CommentPlacement placement) { setComment (comment.c_str (), placement); } bool Value::hasComment (CommentPlacement placement) const { return comments_ != 0 && comments_[placement].comment_ != 0; } std::string Value::getComment (CommentPlacement placement) const { if (hasComment (placement)) return comments_[placement].comment_; return ""; } std::string Value::toStyledString () const { StyledWriter writer; return writer.write (*this); } Value::const_iterator Value::begin () const { switch (type_) { case arrayValue: case objectValue: if (value_.map_) return const_iterator (value_.map_->begin ()); break; default: break; } return const_iterator (); } Value::const_iterator Value::end () const { switch (type_) { case arrayValue: case objectValue: if (value_.map_) return const_iterator (value_.map_->end ()); break; default: break; } return const_iterator (); } Value::iterator Value::begin () { switch (type_) { case arrayValue: case objectValue: if (value_.map_) return iterator (value_.map_->begin ()); break; default: break; } return iterator (); } Value::iterator Value::end () { switch (type_) { case arrayValue: case objectValue: if (value_.map_) return iterator (value_.map_->end ()); break; default: break; } return iterator (); } } // namespace json