blob: 2562b12b766f00eae1ebc1c75a50aadbd77a9365 [file] [log] [blame]
// 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