// ================================================================================ // == This file is a part of Turbo Badger. (C) 2011-2014, Emil Segerås == // == See tb_core.h for more information. == // ================================================================================ #include "tb_value.h" #include "tb_object.h" #include "tb_str.h" #include #include #include #include namespace tb { // FIX: ## Floating point string conversions might be locale dependant. Force "." as decimal! // == Helper functions ============================ char *next_token(char *&str, const char *delim) { str += strspn(str, delim); if (!*str) return nullptr; char *token = str; str += strcspn(str, delim); if (*str) *str++ = '\0'; return token; } bool is_start_of_number(const char *str) { if (*str == '-') str++; if (*str == '.') str++; return *str >= '0' && *str <= '9'; } bool contains_non_trailing_space(const char *str) { if (const char *p = strstr(str, " ")) { while (*p == ' ') p++; return *p != '\0'; } return false; } bool is_number_only(const char *s) { if (!s || *s == 0 || *s == ' ') return 0; char *p; strtod(s, &p); while (*p == ' ') p++; return *p == '\0'; } bool is_number_float(const char *str) { while (*str) if (*str++ == '.') return true; return false; } // == TBValueArray ================================== TBValueArray::TBValueArray() { } TBValueArray::~TBValueArray() { } TBValue *TBValueArray::AddValue() { TBValue *v; if ((v = new TBValue()) && m_list.Add(v)) return v; delete v; return nullptr; } TBValue *TBValueArray::GetValue(int index) { if (index >= 0 && index < m_list.GetNumItems()) return m_list[index]; return nullptr; } TBValueArray *TBValueArray::Clone(TBValueArray *source) { TBValueArray *new_arr = new TBValueArray; if (!new_arr) return nullptr; for (int i = 0; i < source->m_list.GetNumItems(); i++) { TBValue *new_val = new_arr->AddValue(); if (!new_val) { delete new_arr; return nullptr; } new_val->Copy(*source->GetValue(i)); } return new_arr; } // == TBValue ======================================= TBValue::TBValue() : m_packed_init(0) { } TBValue::TBValue(const TBValue &value) : m_packed_init(0) { Copy(value); } TBValue::TBValue(TYPE type) : m_packed_init(0) { switch (type) { case TYPE_NULL: SetNull(); break; case TYPE_STRING: SetString("", SET_AS_STATIC); break; case TYPE_FLOAT: SetFloat(0); break; case TYPE_INT: SetInt(0); break; case TYPE_OBJECT: SetObject(nullptr); break; case TYPE_ARRAY: if (TBValueArray *arr = new TBValueArray()) SetArray(arr, SET_TAKE_OWNERSHIP); break; default: assert(!"Not implemented!"); }; } TBValue::TBValue(int value) : m_packed_init(0) { SetInt(value); } TBValue::TBValue(float value) : m_packed_init(0) { SetFloat(value); } TBValue::TBValue(const char *value, SET set) : m_packed_init(0) { SetString(value, set); } TBValue::TBValue(TBTypedObject *object) : m_packed_init(0) { SetObject(object); } TBValue::~TBValue() { SetNull(); } void TBValue::TakeOver(TBValue &source_value) { if (source_value.m_packed.type == TYPE_STRING) SetString(source_value.val_str, source_value.m_packed.allocated ? SET_TAKE_OWNERSHIP : SET_NEW_COPY); else if (source_value.m_packed.type == TYPE_ARRAY) SetArray(source_value.val_arr, source_value.m_packed.allocated ? SET_TAKE_OWNERSHIP : SET_NEW_COPY); else *this = source_value; source_value.m_packed.type = TYPE_NULL; } void TBValue::Copy(const TBValue &source_value) { if (source_value.m_packed.type == TYPE_STRING) SetString(source_value.val_str, SET_NEW_COPY); else if (source_value.m_packed.type == TYPE_ARRAY) SetArray(source_value.val_arr, SET_NEW_COPY); else if (source_value.m_packed.type == TYPE_OBJECT) { assert(!"We can't copy objects! The value will be nulled!"); SetObject(nullptr); } else { SetNull(); memcpy(this, &source_value, sizeof(TBValue)); } } void TBValue::SetNull() { if (m_packed.allocated) { if (m_packed.type == TYPE_STRING) free(val_str); else if (m_packed.type == TYPE_OBJECT) delete val_obj; else if (m_packed.type == TYPE_ARRAY) delete val_arr; } m_packed.type = TYPE_NULL; } void TBValue::SetInt(int val) { SetNull(); m_packed.type = TYPE_INT; val_int = val; } void TBValue::SetFloat(float val) { SetNull(); m_packed.type = TYPE_FLOAT; val_float = val; } void TBValue::SetString(const char *val, SET set) { SetNull(); m_packed.allocated = (set == SET_NEW_COPY || set == SET_TAKE_OWNERSHIP); if (set != SET_NEW_COPY) { val_str = const_cast(val); m_packed.type = TYPE_STRING; } else if ((val_str = strdup(val))) m_packed.type = TYPE_STRING; } void TBValue::SetObject(TBTypedObject *object) { SetNull(); m_packed.type = TYPE_OBJECT; m_packed.allocated = true; val_obj = object; } void TBValue::SetArray(TBValueArray *arr, SET set) { SetNull(); m_packed.allocated = (set == SET_NEW_COPY || set == SET_TAKE_OWNERSHIP); if (set != SET_NEW_COPY) { val_arr = arr; m_packed.type = TYPE_ARRAY; } else if ((val_arr = TBValueArray::Clone(arr))) m_packed.type = TYPE_ARRAY; } void TBValue::SetFromStringAuto(const char *str, SET set) { if (!str) SetNull(); else if (is_number_only(str)) { if (is_number_float(str)) SetFloat((float)atof(str)); else SetInt(atoi(str)); } else if (is_start_of_number(str) && contains_non_trailing_space(str)) { // If the number has nontrailing space, we'll assume a list of numbers (example: "10 -4 3.5") SetNull(); if (TBValueArray *arr = new TBValueArray) { TBStr tmpstr; if (tmpstr.Set(str)) { char *str_next = tmpstr.CStr(); while (char *token = next_token(str_next, ", ")) { if (TBValue *new_val = arr->AddValue()) new_val->SetFromStringAuto(token, SET_NEW_COPY); } } SetArray(arr, SET_TAKE_OWNERSHIP); } } else if (*str == '[') { SetNull(); if (TBValueArray *arr = new TBValueArray) { assert(!"not implemented! Split out the tokenizer code above!"); SetArray(arr, SET_TAKE_OWNERSHIP); } } else { SetString(str, set); return; } // We didn't set as string, so we might need to deal with the passed in string data. if (set == SET_TAKE_OWNERSHIP) { // Delete the passed in data TBValue tmp; tmp.SetString(str, SET_TAKE_OWNERSHIP); } } int TBValue::GetInt() const { if (m_packed.type == TYPE_STRING) return atoi(val_str); else if (m_packed.type == TYPE_FLOAT) return (int) val_float; return m_packed.type == TYPE_INT ? val_int : 0; } float TBValue::GetFloat() const { if (m_packed.type == TYPE_STRING) return (float) atof(val_str); else if (m_packed.type == TYPE_INT) return (float) val_int; return m_packed.type == TYPE_FLOAT ? val_float : 0; } const char *TBValue::GetString() { if (m_packed.type == TYPE_INT) { char tmp[32]; sprintf(tmp, "%d", val_int); SetString(tmp, SET_NEW_COPY); } else if (m_packed.type == TYPE_FLOAT) { char tmp[32]; sprintf(tmp, "%f", val_float); SetString(tmp, SET_NEW_COPY); } else if (m_packed.type == TYPE_OBJECT) return val_obj ? val_obj->GetClassName() : ""; return m_packed.type == TYPE_STRING ? val_str : ""; } }; // namespace tb