| // Copyright 2019 The ChromiumOS Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "libipp/ipp_attribute.h" |
| |
| #include <algorithm> |
| #include <cassert> |
| #include <iterator> |
| #include <limits> |
| #include <string> |
| #include <vector> |
| |
| #include "frame.h" // needed for ipp::Code |
| |
| namespace { |
| |
| // Maximum size of fields name and value (separately) in TNV. |
| constexpr size_t kMaxSizeOfNameOrValue = std::numeric_limits<int16_t>::max(); |
| |
| ipp::InternalType InternalTypeForUnknownAttribute(ipp::ValueTag type) { |
| switch (type) { |
| case ipp::ValueTag::collection: |
| return ipp::InternalType::kCollection; |
| case ipp::ValueTag::boolean: |
| case ipp::ValueTag::integer: |
| case ipp::ValueTag::enum_: |
| return ipp::InternalType::kInteger; |
| case ipp::ValueTag::dateTime: |
| return ipp::InternalType::kDateTime; |
| case ipp::ValueTag::resolution: |
| return ipp::InternalType::kResolution; |
| case ipp::ValueTag::rangeOfInteger: |
| return ipp::InternalType::kRangeOfInteger; |
| case ipp::ValueTag::nameWithLanguage: |
| case ipp::ValueTag::textWithLanguage: |
| return ipp::InternalType::kStringWithLanguage; |
| default: |
| return ipp::InternalType::kString; |
| } |
| } |
| |
| std::string UnsignedToString(size_t x) { |
| std::string s; |
| do { |
| s.push_back('0' + (x % 10)); |
| x /= 10; |
| } while (x > 0); |
| std::reverse(s.begin(), s.end()); |
| return s; |
| } |
| } // namespace |
| |
| namespace ipp { |
| |
| namespace { |
| |
| // This struct exposes single static method performing conversion between values |
| // of different types. Returns true if conversion succeeded and false otherwise. |
| // |out_val| cannot be nullptr. |
| template <typename InputType, typename OutputType> |
| struct Converter { |
| static bool Convert(const std::string& name, |
| const AttrDef& def, |
| const InputType& in_val, |
| OutputType* out_val) { |
| return false; |
| } |
| }; |
| template <typename Type> |
| struct Converter<Type, Type> { |
| static bool Convert(const std::string& name, |
| const AttrDef& def, |
| const Type& in_val, |
| Type* out_val) { |
| *out_val = in_val; |
| return true; |
| } |
| }; |
| template <> |
| struct Converter<std::string, std::string> { |
| static bool Convert(const std::string& name, |
| const AttrDef& def, |
| const std::string& in_val, |
| std::string* out_val) { |
| *out_val = in_val; |
| return true; |
| } |
| }; |
| template <typename InputType> |
| struct Converter<InputType, std::string> { |
| static bool Convert(const std::string& name, |
| const AttrDef& def, |
| const InputType& in_val, |
| std::string* out_val) { |
| *out_val = ToString(in_val); |
| return true; |
| } |
| }; |
| template <> |
| struct Converter<int32_t, std::string> { |
| static bool Convert(const std::string& name, |
| const AttrDef& def, |
| int32_t in_val, |
| std::string* out_val) { |
| if (def.ipp_type == ValueTag::boolean) { |
| *out_val = ToString(static_cast<bool>(in_val)); |
| } else if (def.ipp_type == ValueTag::enum_ || |
| def.ipp_type == ValueTag::keyword) { |
| AttrName attr_name; |
| if (!FromString(name, &attr_name)) |
| return false; |
| *out_val = ToString(attr_name, in_val); |
| } else if (def.ipp_type == ValueTag::integer) { |
| *out_val = ToString(in_val); |
| } else { |
| return false; |
| } |
| return true; |
| } |
| }; |
| template <> |
| struct Converter<std::string, bool> { |
| static bool Convert(const std::string& name, |
| const AttrDef& def, |
| const std::string& in_val, |
| bool* out_val) { |
| return FromString(in_val, out_val); |
| } |
| }; |
| template <> |
| struct Converter<std::string, int32_t> { |
| static bool Convert(const std::string& name, |
| const AttrDef& def, |
| const std::string& in_val, |
| int32_t* out_val) { |
| bool result = false; |
| if (def.ipp_type == ValueTag::boolean) { |
| bool out; |
| result = FromString(in_val, &out); |
| if (result) |
| *out_val = out; |
| } else if (def.ipp_type == ValueTag::enum_ || |
| def.ipp_type == ValueTag::keyword) { |
| AttrName attr_name; |
| if (!FromString(name, &attr_name)) |
| return false; |
| int out; |
| result = FromString(in_val, attr_name, &out); |
| if (result) |
| *out_val = out; |
| } else if (def.ipp_type == ValueTag::integer) { |
| int out; |
| result = FromString(in_val, &out); |
| if (result) |
| *out_val = out; |
| } |
| return result; |
| } |
| }; |
| template <> |
| struct Converter<std::string, StringWithLanguage> { |
| static bool Convert(const std::string& name, |
| const AttrDef& def, |
| const std::string& in_val, |
| StringWithLanguage* out_val) { |
| out_val->language = ""; |
| out_val->value = in_val; |
| return true; |
| } |
| }; |
| |
| // Creates new value for attribute |def| and saves it as void*. |
| template <typename Type> |
| void* CreateValue(const AttrDef& def) { |
| if (sizeof(Type) <= sizeof(void*) && alignof(Type) <= alignof(void*)) |
| return 0; |
| return new Type(); |
| } |
| |
| // Deletes value saved as void*. |
| template <typename Type> |
| void DeleteValue(void* value) { |
| if (sizeof(Type) <= sizeof(void*) && alignof(Type) <= alignof(void*)) |
| return; |
| delete reinterpret_cast<Type*>(value); |
| } |
| |
| // Returns pointer to a value stored as void*. |
| template <typename Type> |
| Type* ReadValuePtr(void** value) { |
| if (sizeof(Type) <= sizeof(void*) && alignof(Type) <= alignof(void*)) |
| return reinterpret_cast<Type*>(value); |
| return reinterpret_cast<Type*>(*value); |
| } |
| |
| // Const version of the template function above. |
| template <typename Type> |
| const Type* ReadValueConstPtr(void* const* value) { |
| if (sizeof(Type) <= sizeof(void*) && alignof(Type) <= alignof(void*)) |
| return reinterpret_cast<const Type*>(value); |
| return reinterpret_cast<Type* const>(*value); |
| } |
| |
| // Resizes vector of values in an attribute |def|. |
| template <typename Type> |
| void ResizeVector(const AttrDef& def, std::vector<Type>* v, size_t new_size) { |
| v->resize(new_size); |
| } |
| template <> |
| void ResizeVector<Collection*>(const AttrDef& def, |
| std::vector<Collection*>* v, |
| size_t new_size) { |
| const size_t old_size = v->size(); |
| for (size_t i = new_size; i < old_size; ++i) |
| delete v->at(i); |
| v->resize(new_size); |
| for (size_t i = old_size; i < new_size; ++i) |
| (*v)[i] = new Collection; |
| } |
| |
| // Deletes the whole attribute's |values|. |
| template <typename Type> |
| void DeleteAttrTyped(void*& values, const AttrDef& def) { |
| if (values == nullptr) |
| return; |
| auto pv = reinterpret_cast<std::vector<Type>*>(values); |
| ResizeVector<Type>(def, pv, 0); |
| delete pv; |
| values = nullptr; |
| } |
| |
| // The same as previous one, just chooses correct template instantiation. |
| void DeleteAttr(void*& values, const AttrDef& def) { |
| switch (def.cc_type) { |
| case InternalType::kInteger: |
| DeleteAttrTyped<int32_t>(values, def); |
| break; |
| case InternalType::kString: |
| DeleteAttrTyped<std::string>(values, def); |
| break; |
| case InternalType::kResolution: |
| DeleteAttrTyped<Resolution>(values, def); |
| break; |
| case InternalType::kRangeOfInteger: |
| DeleteAttrTyped<RangeOfInteger>(values, def); |
| break; |
| case InternalType::kDateTime: |
| DeleteAttrTyped<DateTime>(values, def); |
| break; |
| case InternalType::kStringWithLanguage: |
| DeleteAttrTyped<StringWithLanguage>(values, def); |
| break; |
| case InternalType::kCollection: |
| DeleteAttrTyped<Collection*>(values, def); |
| break; |
| } |
| } |
| |
| // Returns a pointer to a value at position |index| in an attribute's |values|. |
| // If the attribute is too short, it is resized to (|index|+1) when possible. |
| // When |cut_if_longer| is set, the attribute is shrunk to (|index|+1) values if |
| // it is longer. If |cut_if_longer| equals false, the attribute is no |
| // downsized. The function never returns nullptr. |
| template <typename Type> |
| Type* ResizeAttrGetValuePtr(void*& values, |
| const AttrDef& def, |
| size_t index, |
| bool cut_if_longer) { |
| // Create |values| if not exists. |
| if (values == nullptr) { |
| values = new std::vector<Type>(); |
| } |
| // Returns the pointer, resize the attribute when needed. |
| std::vector<Type>* v = reinterpret_cast<std::vector<Type>*>(values); |
| if (cut_if_longer || v->size() <= index) |
| ResizeVector<Type>(def, v, index + 1); |
| return (v->data() + index); |
| } |
| |
| // Resizes an attribute's |values| to |new_size| values. The parameter |
| // |cut_if_longer| works in the same way as in the previous template function. |
| void ResizeAttr(void*& values, |
| const AttrDef& def, |
| size_t new_size, |
| bool cut_if_longer) { |
| if (new_size == 0) { |
| DeleteAttr(values, def); |
| return; |
| } |
| switch (def.cc_type) { |
| case InternalType::kInteger: |
| ResizeAttrGetValuePtr<int32_t>(values, def, new_size - 1, cut_if_longer); |
| break; |
| case InternalType::kString: |
| ResizeAttrGetValuePtr<std::string>(values, def, new_size - 1, |
| cut_if_longer); |
| break; |
| case InternalType::kResolution: |
| ResizeAttrGetValuePtr<Resolution>(values, def, new_size - 1, |
| cut_if_longer); |
| break; |
| case InternalType::kRangeOfInteger: |
| ResizeAttrGetValuePtr<RangeOfInteger>(values, def, new_size - 1, |
| cut_if_longer); |
| break; |
| case InternalType::kDateTime: |
| ResizeAttrGetValuePtr<DateTime>(values, def, new_size - 1, cut_if_longer); |
| break; |
| case InternalType::kStringWithLanguage: |
| ResizeAttrGetValuePtr<StringWithLanguage>(values, def, new_size - 1, |
| cut_if_longer); |
| break; |
| case InternalType::kCollection: |
| ResizeAttrGetValuePtr<Collection*>(values, def, new_size - 1, |
| cut_if_longer); |
| break; |
| } |
| } |
| |
| // Reads a value at position |index| in an attribute's |values| and saves it |
| // to |value|. Proper conversion is applied when needed. The function returns |
| // true if succeeds and false when one of the following occurs: |
| // * |value| is nullptr |
| // * the attribute has less than |index|+1 values |
| // * required conversion is not possible |
| template <typename InternalType, typename ApiType> |
| bool ReadConvertValueTyped(void* const& values, |
| const std::string& name, |
| const AttrDef& def, |
| size_t index, |
| ApiType* value) { |
| if (value == nullptr) |
| return false; |
| |
| const InternalType* internal_value = nullptr; |
| auto v = ReadValueConstPtr<std::vector<InternalType>>(&values); |
| if (v->size() <= index) |
| return false; |
| internal_value = v->data() + index; |
| return Converter<InternalType, ApiType>::Convert(name, def, *internal_value, |
| value); |
| } |
| |
| // The same as previous one, just chooses correct template instantiation. |
| template <typename ApiType> |
| bool ReadConvertValue(void* const& values, |
| const std::string& name, |
| const AttrDef& def, |
| size_t index, |
| ApiType* value) { |
| switch (def.cc_type) { |
| case InternalType::kInteger: |
| return ReadConvertValueTyped<int32_t, ApiType>(values, name, def, index, |
| value); |
| case InternalType::kString: |
| return ReadConvertValueTyped<std::string, ApiType>(values, name, def, |
| index, value); |
| case InternalType::kResolution: |
| return ReadConvertValueTyped<Resolution, ApiType>(values, name, def, |
| index, value); |
| case InternalType::kRangeOfInteger: |
| return ReadConvertValueTyped<RangeOfInteger, ApiType>(values, name, def, |
| index, value); |
| case InternalType::kDateTime: |
| return ReadConvertValueTyped<DateTime, ApiType>(values, name, def, index, |
| value); |
| case InternalType::kStringWithLanguage: |
| return ReadConvertValueTyped<StringWithLanguage, ApiType>( |
| values, name, def, index, value); |
| case InternalType::kCollection: |
| return false; |
| } |
| return false; |
| } |
| |
| // Saves |value| to position |index| in an attribute |name|,|def|. Proper |
| // conversion is applied when needed. The attribute is also resized when |index| |
| // is greater than the attribute's size. The function returns true if succeeds |
| // and false when the required conversion is not possible. |
| template <typename InternalType, typename ApiType> |
| bool SaveValueTyped(void*& values, |
| const std::string& name, |
| const AttrDef& def, |
| size_t index, |
| const ApiType& value) { |
| InternalType internal_value; |
| if (!Converter<ApiType, InternalType>::Convert(name, def, value, |
| &internal_value)) |
| return false; |
| InternalType* internal_ptr = |
| ResizeAttrGetValuePtr<InternalType>(values, def, index, false); |
| *internal_ptr = internal_value; |
| return true; |
| } |
| |
| } // end of namespace |
| |
| std::string_view ToStrView(ValueTag tag) { |
| switch (tag) { |
| case ValueTag::unsupported: |
| return std::string_view("unsupported"); |
| case ValueTag::unknown: |
| return std::string_view("unknown"); |
| case ValueTag::no_value: |
| return std::string_view("no-value"); |
| case ValueTag::not_settable: |
| return std::string_view("not-settable"); |
| case ValueTag::delete_attribute: |
| return std::string_view("delete-attribute"); |
| case ValueTag::admin_define: |
| return std::string_view("admin-define"); |
| case ValueTag::integer: |
| return std::string_view("integer"); |
| case ValueTag::boolean: |
| return std::string_view("boolean"); |
| case ValueTag::enum_: |
| return std::string_view("enum"); |
| case ValueTag::octetString: |
| return std::string_view("octetString"); |
| case ValueTag::dateTime: |
| return std::string_view("dateTime"); |
| case ValueTag::resolution: |
| return std::string_view("resolution"); |
| case ValueTag::rangeOfInteger: |
| return std::string_view("rangeOfInteger"); |
| case ValueTag::collection: |
| return std::string_view("collection"); |
| case ValueTag::textWithLanguage: |
| return std::string_view("textWithLanguage"); |
| case ValueTag::nameWithLanguage: |
| return std::string_view("nameWithLanguage"); |
| case ValueTag::textWithoutLanguage: |
| return std::string_view("textWithoutLanguage"); |
| case ValueTag::nameWithoutLanguage: |
| return std::string_view("nameWithoutLanguage"); |
| case ValueTag::keyword: |
| return std::string_view("keyword"); |
| case ValueTag::uri: |
| return std::string_view("uri"); |
| case ValueTag::uriScheme: |
| return std::string_view("uriScheme"); |
| case ValueTag::charset: |
| return std::string_view("charset"); |
| case ValueTag::naturalLanguage: |
| return std::string_view("naturalLanguage"); |
| case ValueTag::mimeMediaType: |
| return std::string_view("mimeMediaType"); |
| } |
| if (IsValid(tag)) { |
| return std::string_view("<unknown_ValueTag>"); |
| } |
| return std::string_view("<invalid_ValueTag>"); |
| } |
| |
| std::string ToString(bool v) { |
| return (v ? "true" : "false"); |
| } |
| |
| std::string ToString(int v) { |
| if (v < 0) { |
| // 2 x incrementation in case of (v == numeric_limit<int>::min()) |
| const std::string s = UnsignedToString(static_cast<size_t>(-(++v)) + 1); |
| return "-" + s; |
| } |
| return UnsignedToString(v); |
| } |
| |
| std::string ToString(const Resolution& v) { |
| std::string s = ToString(v.xres) + "x" + ToString(v.yres); |
| if (v.units == Resolution::kDotsPerInch) |
| s += "dpi"; |
| else |
| s += "dpc"; |
| return s; |
| } |
| |
| std::string ToString(const RangeOfInteger& v) { |
| return ("(" + ToString(v.min_value) + ":" + ToString(v.max_value) + ")"); |
| } |
| |
| std::string ToString(const DateTime& v) { |
| return (ToString(v.year) + "-" + ToString(v.month) + "-" + ToString(v.day) + |
| "," + ToString(v.hour) + ":" + ToString(v.minutes) + ":" + |
| ToString(v.seconds) + "." + ToString(v.deci_seconds) + "," + |
| std::string(1, v.UTC_direction) + ToString(v.UTC_hours) + ":" + |
| ToString(v.UTC_minutes)); |
| } |
| |
| std::string ToString(const StringWithLanguage& value) { |
| return value.value; |
| } |
| |
| bool FromString(const std::string& s, bool* v) { |
| if (v == nullptr) |
| return false; |
| if (s == "false") { |
| *v = false; |
| return true; |
| } |
| if (s == "true") { |
| *v = true; |
| return true; |
| } |
| return false; |
| } |
| |
| // JSON-like integer format: first character may be '-', the rest must be |
| // digits. Leading zeroes allowed. |
| bool FromString(const std::string& s, int* out) { |
| if (out == nullptr) |
| return false; |
| if (s.empty()) |
| return false; |
| auto it = s.begin(); |
| int vv = 0; |
| if (*it == '-') { |
| ++it; |
| if (it == s.end()) |
| return false; |
| // negative number |
| for (; it != s.end(); ++it) { |
| if (std::numeric_limits<int>::min() / 10 > vv) |
| return false; |
| vv *= 10; |
| if (*it < '0' || *it > '9') |
| return false; |
| const int d = (*it - '0'); |
| if (std::numeric_limits<int>::min() + d > vv) |
| return false; |
| vv -= d; |
| } |
| } else { |
| // positive number |
| for (; it != s.end(); ++it) { |
| if (std::numeric_limits<int>::max() / 10 < vv) |
| return false; |
| vv *= 10; |
| if (*it < '0' || *it > '9') |
| return false; |
| const int d = (*it - '0'); |
| if (std::numeric_limits<int>::max() - d < vv) |
| return false; |
| vv += d; |
| } |
| } |
| *out = vv; |
| return true; |
| } |
| |
| Attribute::~Attribute() { |
| DeleteAttr(values_, def_); |
| } |
| |
| ValueTag Attribute::Tag() const { |
| return def_.ipp_type; |
| } |
| |
| Attribute::Attribute(std::string_view name, AttrDef def) |
| : name_(name), def_(def) { |
| // Attributes with non-out-of-band tag must have at least one value. |
| if (!IsOutOfBand(def.ipp_type)) { |
| ResizeAttr(values_, def_, 1, false); |
| } |
| } |
| |
| std::string_view Attribute::Name() const { |
| return name_; |
| } |
| |
| size_t Attribute::GetSize() const { |
| if (values_ == nullptr) |
| return 0; |
| switch (def_.cc_type) { |
| case InternalType::kInteger: |
| return ReadValueConstPtr<std::vector<int32_t>>(&values_)->size(); |
| case InternalType::kString: |
| return ReadValueConstPtr<std::vector<std::string>>(&values_)->size(); |
| case InternalType::kResolution: |
| return ReadValueConstPtr<std::vector<Resolution>>(&values_)->size(); |
| case InternalType::kRangeOfInteger: |
| return ReadValueConstPtr<std::vector<RangeOfInteger>>(&values_)->size(); |
| case InternalType::kDateTime: |
| return ReadValueConstPtr<std::vector<DateTime>>(&values_)->size(); |
| case InternalType::kStringWithLanguage: |
| return ReadValueConstPtr<std::vector<StringWithLanguage>>(&values_) |
| ->size(); |
| case InternalType::kCollection: |
| return ReadValueConstPtr<std::vector<Collection*>>(&values_)->size(); |
| } |
| return 0; |
| } |
| |
| size_t Attribute::Size() const { |
| return GetSize(); |
| } |
| |
| void Attribute::Resize(size_t new_size) { |
| if (IsOutOfBand(def_.ipp_type) || new_size == 0) |
| return; |
| ResizeAttr(values_, def_, new_size, true); |
| } |
| |
| Code Attribute::GetValue(size_t index, bool& val) const { |
| if (def_.ipp_type != ValueTag::boolean) |
| return Code::kIncompatibleType; |
| auto values = ReadValueConstPtr<std::vector<int32_t>>(&values_); |
| if (index >= values->size()) |
| return Code::kIndexOutOfRange; |
| val = values->at(index); |
| return Code::kOK; |
| } |
| |
| Code Attribute::GetValue(size_t index, int32_t& val) const { |
| if (!IsInteger(def_.ipp_type)) |
| return Code::kIncompatibleType; |
| auto values = ReadValueConstPtr<std::vector<int32_t>>(&values_); |
| if (index >= values->size()) |
| return Code::kIndexOutOfRange; |
| val = values->at(index); |
| return Code::kOK; |
| } |
| |
| Code Attribute::GetValue(size_t index, std::string& val) const { |
| if (!IsString(def_.ipp_type) && def_.ipp_type != ValueTag::octetString) |
| return Code::kIncompatibleType; |
| auto values = ReadValueConstPtr<std::vector<std::string>>(&values_); |
| if (index >= values->size()) |
| return Code::kIndexOutOfRange; |
| val = values->at(index); |
| return Code::kOK; |
| } |
| |
| Code Attribute::GetValue(size_t index, StringWithLanguage& val) const { |
| if (def_.ipp_type == ValueTag::nameWithLanguage || |
| def_.ipp_type == ValueTag::textWithLanguage) { |
| auto values = ReadValueConstPtr<std::vector<StringWithLanguage>>(&values_); |
| if (index >= values->size()) |
| return Code::kIndexOutOfRange; |
| val = values->at(index); |
| return Code::kOK; |
| } |
| if (def_.ipp_type == ValueTag::nameWithoutLanguage || |
| def_.ipp_type == ValueTag::textWithoutLanguage) { |
| auto values = ReadValueConstPtr<std::vector<std::string>>(&values_); |
| if (index >= values->size()) |
| return Code::kIndexOutOfRange; |
| val.value = values->at(index); |
| val.language = ""; |
| return Code::kOK; |
| } |
| return Code::kIncompatibleType; |
| } |
| |
| Code Attribute::GetValue(size_t index, DateTime& val) const { |
| if (def_.ipp_type != ValueTag::dateTime) |
| return Code::kIncompatibleType; |
| auto values = ReadValueConstPtr<std::vector<DateTime>>(&values_); |
| if (index >= values->size()) |
| return Code::kIndexOutOfRange; |
| val = values->at(index); |
| return Code::kOK; |
| } |
| |
| Code Attribute::GetValue(size_t index, Resolution& val) const { |
| if (def_.ipp_type != ValueTag::resolution) |
| return Code::kIncompatibleType; |
| auto values = ReadValueConstPtr<std::vector<Resolution>>(&values_); |
| if (index >= values->size()) |
| return Code::kIndexOutOfRange; |
| val = values->at(index); |
| return Code::kOK; |
| } |
| |
| Code Attribute::GetValue(size_t index, RangeOfInteger& val) const { |
| if (def_.ipp_type == ValueTag::rangeOfInteger) { |
| auto values = ReadValueConstPtr<std::vector<RangeOfInteger>>(&values_); |
| if (index >= values->size()) |
| return Code::kIndexOutOfRange; |
| val = values->at(index); |
| return Code::kOK; |
| } |
| if (def_.ipp_type == ValueTag::integer) { |
| auto values = ReadValueConstPtr<std::vector<int32_t>>(&values_); |
| if (index >= values->size()) |
| return Code::kIndexOutOfRange; |
| val.min_value = val.max_value = values->at(index); |
| return Code::kOK; |
| } |
| return Code::kIncompatibleType; |
| } |
| |
| CollsView Attribute::Colls() { |
| if (Tag() != ValueTag::collection || Size() == 0) { |
| return CollsView(); |
| } |
| return CollsView(*ReadValuePtr<std::vector<Collection*>>(&values_)); |
| } |
| |
| Code Attribute::GetValues(std::vector<bool>& values) const { |
| if (def_.ipp_type != ValueTag::boolean) |
| return Code::kIncompatibleType; |
| const std::vector<int32_t>* vints = |
| ReadValueConstPtr<std::vector<int32_t>>(&values_); |
| values.assign(vints->begin(), vints->end()); |
| return Code::kOK; |
| } |
| |
| Code Attribute::GetValues(std::vector<int32_t>& values) const { |
| if (!IsInteger(def_.ipp_type)) |
| return Code::kIncompatibleType; |
| values = *ReadValueConstPtr<std::vector<int32_t>>(&values_); |
| return Code::kOK; |
| } |
| |
| Code Attribute::GetValues(std::vector<std::string>& values) const { |
| if (!IsString(def_.ipp_type) && def_.ipp_type != ValueTag::octetString) |
| return Code::kIncompatibleType; |
| values = *ReadValueConstPtr<std::vector<std::string>>(&values_); |
| return Code::kOK; |
| } |
| |
| Code Attribute::GetValues(std::vector<StringWithLanguage>& values) const { |
| if (def_.ipp_type == ValueTag::nameWithLanguage || |
| def_.ipp_type == ValueTag::textWithLanguage) { |
| values = *ReadValueConstPtr<std::vector<StringWithLanguage>>(&values_); |
| return Code::kOK; |
| } |
| if (def_.ipp_type == ValueTag::nameWithoutLanguage || |
| def_.ipp_type == ValueTag::textWithoutLanguage) { |
| auto org_values = ReadValueConstPtr<std::vector<std::string>>(&values_); |
| values.resize(org_values->size()); |
| for (size_t i = 0; i < values.size(); ++i) { |
| values[i].value = (*org_values)[i]; |
| values[i].language.clear(); |
| } |
| return Code::kOK; |
| } |
| return Code::kIncompatibleType; |
| } |
| |
| Code Attribute::GetValues(std::vector<DateTime>& values) const { |
| if (def_.ipp_type != ValueTag::dateTime) |
| return Code::kIncompatibleType; |
| values = *ReadValueConstPtr<std::vector<DateTime>>(&values_); |
| return Code::kOK; |
| } |
| |
| Code Attribute::GetValues(std::vector<Resolution>& values) const { |
| if (def_.ipp_type != ValueTag::resolution) |
| return Code::kIncompatibleType; |
| values = *ReadValueConstPtr<std::vector<Resolution>>(&values_); |
| return Code::kOK; |
| } |
| |
| Code Attribute::GetValues(std::vector<RangeOfInteger>& values) const { |
| if (def_.ipp_type == ValueTag::rangeOfInteger) { |
| values = *ReadValueConstPtr<std::vector<RangeOfInteger>>(&values_); |
| return Code::kOK; |
| } |
| if (def_.ipp_type == ValueTag::integer) { |
| auto org_values = ReadValueConstPtr<std::vector<int32_t>>(&values_); |
| values.resize(org_values->size()); |
| for (size_t i = 0; i < values.size(); ++i) { |
| values[i].min_value = values[i].max_value = (*org_values)[i]; |
| } |
| return Code::kOK; |
| } |
| return Code::kIncompatibleType; |
| } |
| |
| Code Attribute::SetValues(bool value) { |
| return SetValues(std::vector<bool>{value}); |
| } |
| |
| Code Attribute::SetValues(int32_t value) { |
| return SetValues(std::vector<int32_t>{value}); |
| } |
| |
| Code Attribute::SetValues(const std::string& value) { |
| return SetValues(std::vector<std::string>{value}); |
| } |
| |
| Code Attribute::SetValues(const StringWithLanguage& value) { |
| return SetValues(std::vector<StringWithLanguage>{value}); |
| } |
| |
| Code Attribute::SetValues(DateTime value) { |
| return SetValues(std::vector<DateTime>{value}); |
| } |
| |
| Code Attribute::SetValues(Resolution value) { |
| return SetValues(std::vector<Resolution>{value}); |
| } |
| |
| Code Attribute::SetValues(RangeOfInteger value) { |
| return SetValues(std::vector<RangeOfInteger>{value}); |
| } |
| |
| Code Attribute::SetValues(const std::vector<bool>& values) { |
| if (def_.ipp_type != ValueTag::boolean) |
| return Code::kIncompatibleType; |
| std::vector<int32_t>* vints = ReadValuePtr<std::vector<int32_t>>(&values_); |
| vints->assign(values.begin(), values.end()); |
| return Code::kOK; |
| } |
| |
| Code Attribute::SetValues(const std::vector<int32_t>& values) { |
| switch (def_.ipp_type) { |
| case ValueTag::boolean: |
| for (int32_t v : values) |
| if (v < 0 || v > 1) |
| return Code::kValueOutOfRange; |
| [[fallthrough]]; |
| case ValueTag::enum_: |
| case ValueTag::integer: |
| *ReadValuePtr<std::vector<int32_t>>(&values_) = values; |
| return Code::kOK; |
| default: |
| return Code::kIncompatibleType; |
| } |
| } |
| |
| Code Attribute::SetValues(const std::vector<std::string>& values) { |
| if (!IsString(def_.ipp_type) && def_.ipp_type != ValueTag::octetString) |
| return Code::kIncompatibleType; |
| for (const std::string& v : values) { |
| if (v.size() > kMaxSizeOfNameOrValue) |
| return Code::kValueOutOfRange; |
| } |
| *ReadValuePtr<std::vector<std::string>>(&values_) = values; |
| return Code::kOK; |
| } |
| |
| Code Attribute::SetValues(const std::vector<StringWithLanguage>& values) { |
| if (def_.ipp_type != ValueTag::nameWithLanguage && |
| def_.ipp_type != ValueTag::textWithLanguage) { |
| return Code::kIncompatibleType; |
| } |
| for (const StringWithLanguage& v : values) { |
| // nameWithLanguage and textWithLanguage values are saved as a sequence of |
| // the following fields (see section 3.9 from rfc8010): |
| // * int16_t (2 bytes) - length of the language field = L |
| // * string (L bytes) - content of the language field |
| // * int16_t (2 bytes) - length of the value field = V |
| // * string (V bytes) - content of the value field |
| // The total size (2 + L + 2 + V) cannot exceed the threshold. |
| if (v.value.size() + v.language.size() + 4 > kMaxSizeOfNameOrValue) |
| return Code::kValueOutOfRange; |
| } |
| *ReadValuePtr<std::vector<StringWithLanguage>>(&values_) = values; |
| return Code::kOK; |
| } |
| |
| Code Attribute::SetValues(const std::vector<DateTime>& values) { |
| if (def_.ipp_type != ValueTag::dateTime) |
| return Code::kIncompatibleType; |
| *ReadValuePtr<std::vector<DateTime>>(&values_) = values; |
| return Code::kOK; |
| } |
| |
| Code Attribute::SetValues(const std::vector<Resolution>& values) { |
| if (def_.ipp_type != ValueTag::resolution) |
| return Code::kIncompatibleType; |
| *ReadValuePtr<std::vector<Resolution>>(&values_) = values; |
| return Code::kOK; |
| } |
| |
| Code Attribute::SetValues(const std::vector<RangeOfInteger>& values) { |
| if (def_.ipp_type != ValueTag::rangeOfInteger) |
| return Code::kIncompatibleType; |
| *ReadValuePtr<std::vector<RangeOfInteger>>(&values_) = values; |
| return Code::kOK; |
| } |
| |
| ConstCollsView Attribute::Colls() const { |
| if (Tag() != ValueTag::collection || Size() == 0) { |
| return ConstCollsView(); |
| } |
| return ConstCollsView(*ReadValueConstPtr<std::vector<Collection*>>(&values_)); |
| } |
| |
| Collection::Collection() = default; |
| |
| Collection::~Collection() = default; |
| |
| Collection::iterator Collection::GetAttr(std::string_view name) { |
| auto it = attributes_index_.find(name); |
| if (it == attributes_index_.end()) |
| return iterator(attributes_.end()); |
| auto it2 = attributes_.begin(); |
| std::advance(it2, it->second); |
| return iterator(it2); |
| } |
| |
| Collection::const_iterator Collection::GetAttr(std::string_view name) const { |
| auto it = attributes_index_.find(name); |
| if (it == attributes_index_.end()) |
| return const_iterator(attributes_.end()); |
| auto it2 = attributes_.begin(); |
| std::advance(it2, it->second); |
| return const_iterator(it2); |
| } |
| |
| Code Collection::CreateNewAttribute(const std::string& name, |
| ValueTag type, |
| Attribute*& new_attr) { |
| // Check all constraints. |
| if (name.empty() || name.size() > kMaxSizeOfNameOrValue) { |
| return Code::kInvalidName; |
| } |
| if (attributes_index_.count(name)) { |
| return Code::kNameConflict; |
| } |
| if (!IsValid(type)) |
| return Code::kInvalidValueTag; |
| // Create new attribute. |
| AttrDef def; |
| def.ipp_type = type; |
| def.cc_type = InternalTypeForUnknownAttribute(type); |
| new_attr = attributes_.emplace_back(new Attribute(name, def)).get(); |
| attributes_index_[new_attr->Name()] = attributes_.size() - 1; |
| return Code::kOK; |
| } |
| |
| template <typename ApiType> |
| Code Collection::AddAttributeToCollection(const std::string& name, |
| ValueTag tag, |
| const std::vector<ApiType>& values) { |
| if (values.empty() && !IsOutOfBand(tag)) { |
| return Code::kValueOutOfRange; |
| } |
| |
| // Create a new attribute. For non-Out-Of-Band tags set the values. |
| Attribute* attr = nullptr; |
| if (Code result = CreateNewAttribute(name, tag, attr); result != Code::kOK) { |
| return result; |
| } |
| if (!IsOutOfBand(tag)) { |
| attr->SetValues(values); |
| } |
| |
| return Code::kOK; |
| } |
| |
| Code Collection::AddAttr(const std::string& name, ValueTag tag) { |
| if (IsOutOfBand(tag)) { |
| return AddAttributeToCollection(name, tag, std::vector<int32_t>()); |
| } |
| return IsValid(tag) ? Code::kIncompatibleType : Code::kInvalidValueTag; |
| } |
| |
| Code Collection::AddAttr(const std::string& name, ValueTag tag, int32_t value) { |
| return AddAttr(name, tag, std::vector<int32_t>{value}); |
| } |
| |
| Code Collection::AddAttr(const std::string& name, |
| ValueTag tag, |
| const std::string& value) { |
| return AddAttr(name, tag, std::vector<std::string>{value}); |
| } |
| |
| Code Collection::AddAttr(const std::string& name, |
| ValueTag tag, |
| const StringWithLanguage& value) { |
| return AddAttr(name, tag, std::vector<StringWithLanguage>{value}); |
| } |
| |
| Code Collection::AddAttr(const std::string& name, |
| ValueTag tag, |
| DateTime value) { |
| return AddAttr(name, tag, std::vector<DateTime>{value}); |
| } |
| |
| Code Collection::AddAttr(const std::string& name, |
| ValueTag tag, |
| Resolution value) { |
| return AddAttr(name, tag, std::vector<Resolution>{value}); |
| } |
| |
| Code Collection::AddAttr(const std::string& name, |
| ValueTag tag, |
| RangeOfInteger value) { |
| return AddAttr(name, tag, std::vector<RangeOfInteger>{value}); |
| } |
| |
| Code Collection::AddAttr(const std::string& name, bool value) { |
| return AddAttr(name, std::vector<bool>{value}); |
| } |
| |
| Code Collection::AddAttr(const std::string& name, int32_t value) { |
| return AddAttr(name, std::vector<int32_t>{value}); |
| } |
| |
| Code Collection::AddAttr(const std::string& name, DateTime value) { |
| return AddAttr(name, std::vector<DateTime>{value}); |
| } |
| |
| Code Collection::AddAttr(const std::string& name, Resolution value) { |
| return AddAttr(name, std::vector<Resolution>{value}); |
| } |
| |
| Code Collection::AddAttr(const std::string& name, RangeOfInteger value) { |
| return AddAttr(name, std::vector<RangeOfInteger>{value}); |
| } |
| |
| Code Collection::AddAttr(const std::string& name, |
| ValueTag tag, |
| const std::vector<int32_t>& values) { |
| switch (tag) { |
| case ValueTag::integer: |
| break; |
| case ValueTag::enum_: // see rfc8011-5.1.5 |
| for (int32_t v : values) { |
| if (v < 1 || v > std::numeric_limits<int16_t>::max()) { |
| return Code::kValueOutOfRange; |
| } |
| } |
| break; |
| case ValueTag::boolean: |
| for (int32_t v : values) { |
| if (v != 0 && v != 1) { |
| return Code::kValueOutOfRange; |
| } |
| } |
| break; |
| default: |
| return IsValid(tag) ? Code::kIncompatibleType : Code::kInvalidValueTag; |
| } |
| return AddAttributeToCollection(name, tag, values); |
| } |
| |
| Code Collection::AddAttr(const std::string& name, |
| ValueTag tag, |
| const std::vector<std::string>& values) { |
| if (tag == ValueTag::octetString || IsString(tag)) { |
| for (const std::string& value : values) { |
| if (value.size() > kMaxSizeOfNameOrValue) |
| return Code::kValueOutOfRange; |
| } |
| return AddAttributeToCollection(name, tag, values); |
| } |
| return IsValid(tag) ? Code::kIncompatibleType : Code::kInvalidValueTag; |
| } |
| |
| Code Collection::AddAttr(const std::string& name, |
| ValueTag tag, |
| const std::vector<StringWithLanguage>& values) { |
| if (tag == ValueTag::nameWithLanguage || tag == ValueTag::textWithLanguage) { |
| for (const StringWithLanguage& value : values) { |
| // nameWithLanguage and textWithLanguage values are saved as a sequence of |
| // the following fields (see section 3.9 from rfc8010): |
| // * int16_t (2 bytes) - length of the language field = L |
| // * string (L bytes) - content of the language field |
| // * int16_t (2 bytes) - length of the value field = V |
| // * string (V bytes) - content of the value field |
| // The total size (2 + L + 2 + V) cannot exceed the threshold. |
| if (value.value.size() + value.language.size() + 4 > |
| kMaxSizeOfNameOrValue) |
| return Code::kValueOutOfRange; |
| } |
| return AddAttributeToCollection(name, tag, values); |
| } |
| return IsValid(tag) ? Code::kIncompatibleType : Code::kInvalidValueTag; |
| } |
| |
| Code Collection::AddAttr(const std::string& name, |
| ValueTag tag, |
| const std::vector<DateTime>& values) { |
| if (tag == ValueTag::dateTime) { |
| return AddAttributeToCollection(name, tag, values); |
| } |
| return IsValid(tag) ? Code::kIncompatibleType : Code::kInvalidValueTag; |
| } |
| |
| Code Collection::AddAttr(const std::string& name, |
| ValueTag tag, |
| const std::vector<Resolution>& values) { |
| if (tag == ValueTag::resolution) { |
| return AddAttributeToCollection(name, tag, values); |
| } |
| return IsValid(tag) ? Code::kIncompatibleType : Code::kInvalidValueTag; |
| } |
| |
| Code Collection::AddAttr(const std::string& name, |
| ValueTag tag, |
| const std::vector<RangeOfInteger>& values) { |
| if (tag == ValueTag::rangeOfInteger) { |
| return AddAttributeToCollection(name, tag, values); |
| } |
| return IsValid(tag) ? Code::kIncompatibleType : Code::kInvalidValueTag; |
| } |
| |
| Code Collection::AddAttr(const std::string& name, |
| const std::vector<bool>& values) { |
| return AddAttributeToCollection(name, ValueTag::boolean, values); |
| } |
| |
| Code Collection::AddAttr(const std::string& name, |
| const std::vector<int32_t>& values) { |
| return AddAttributeToCollection(name, ValueTag::integer, values); |
| } |
| |
| Code Collection::AddAttr(const std::string& name, |
| const std::vector<DateTime>& values) { |
| return AddAttributeToCollection(name, ValueTag::dateTime, values); |
| } |
| |
| Code Collection::AddAttr(const std::string& name, |
| const std::vector<Resolution>& values) { |
| return AddAttributeToCollection(name, ValueTag::resolution, values); |
| } |
| |
| Code Collection::AddAttr(const std::string& name, |
| const std::vector<RangeOfInteger>& values) { |
| return AddAttributeToCollection(name, ValueTag::rangeOfInteger, values); |
| } |
| |
| Code Collection::AddAttr(const std::string& name, CollsView::iterator& coll) { |
| CollsView colls; |
| Code code = AddAttr(name, 1, colls); |
| if (code == Code::kOK) |
| coll = colls.begin(); |
| return code; |
| } |
| |
| Code Collection::AddAttr(const std::string& name, |
| size_t size, |
| CollsView& colls) { |
| if (size == 0) { |
| return Code::kValueOutOfRange; |
| } |
| // Create the attribute and retrieve the pointers. |
| Attribute* attr = nullptr; |
| if (Code result = CreateNewAttribute(name, ValueTag::collection, attr); |
| result != Code::kOK) { |
| return result; |
| } |
| attr->Resize(size); |
| |
| colls = attr->Colls(); |
| return Code::kOK; |
| } |
| |
| // Saves |value| at position |index|. Proper conversion is applied when needed. |
| // The attribute is also resized when |index| is greater than the attribute's |
| // size. The function returns true if succeeds and false when the required |
| // conversion is not possible (|value| is incorrect). |
| template <typename ApiType> |
| bool Attribute::SaveValue(size_t index, const ApiType& value) { |
| if (IsOutOfBand(def_.ipp_type)) |
| return false; |
| bool result = false; |
| switch (def_.cc_type) { |
| case InternalType::kInteger: |
| result = |
| SaveValueTyped<int32_t, ApiType>(values_, name_, def_, index, value); |
| break; |
| case InternalType::kString: |
| result = SaveValueTyped<std::string, ApiType>(values_, name_, def_, index, |
| value); |
| break; |
| case InternalType::kResolution: |
| result = SaveValueTyped<Resolution, ApiType>(values_, name_, def_, index, |
| value); |
| break; |
| case InternalType::kRangeOfInteger: |
| result = SaveValueTyped<RangeOfInteger, ApiType>(values_, name_, def_, |
| index, value); |
| break; |
| case InternalType::kDateTime: |
| result = |
| SaveValueTyped<DateTime, ApiType>(values_, name_, def_, index, value); |
| break; |
| case InternalType::kStringWithLanguage: |
| result = SaveValueTyped<StringWithLanguage, ApiType>(values_, name_, def_, |
| index, value); |
| break; |
| case InternalType::kCollection: |
| return false; |
| } |
| return result; |
| } |
| |
| } // namespace ipp |