| // 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. |
| |
| #ifndef LIBIPP_IPP_ATTRIBUTE_H_ |
| #define LIBIPP_IPP_ATTRIBUTE_H_ |
| |
| #include <cstdint> |
| #include <iterator> |
| #include <limits> |
| #include <map> |
| #include <memory> |
| #include <string> |
| #include <string_view> |
| #include <unordered_map> |
| #include <utility> |
| #include <vector> |
| |
| #include "colls_view.h" |
| #include "ipp_enums.h" |
| #include "ipp_export.h" |
| |
| namespace ipp { |
| |
| // Forward declaration |
| enum class Code; |
| |
| // Values of ValueTag enum are copied from IPP specification; this is why |
| // they do not follow the standard naming rule. |
| // ValueTag defines type of an attribute. It is also called as `syntax` in the |
| // IPP specification. All valid tags are listed below. Values of attributes with |
| // these tags are mapped to C++ types. |
| enum class ValueTag : uint8_t { |
| // 0x00-0x0f are invalid. |
| // 0x10-0x1f are Out-of-Band tags. Attributes with this tag have no values. |
| // All tags from the range 0x10-0x1f are valid. |
| unsupported = 0x10, // [rfc8010] |
| unknown = 0x12, // [rfc8010] |
| no_value = 0x13, // [rfc8010] |
| not_settable = 0x15, // [rfc3380] |
| delete_attribute = 0x16, // [rfc3380] |
| admin_define = 0x17, // [rfc3380] |
| // 0x20-0x2f represents integer types. |
| // Only the following tags are valid. They map to int32_t. |
| integer = 0x21, |
| boolean = 0x22, // maps to both int32_t and bool. |
| enum_ = 0x23, |
| // 0x30-0x3f are called "octetString types". They map to dedicated types. |
| // Only the following tags are valid. |
| octetString = 0x30, // maps to std::string |
| dateTime = 0x31, // maps to DateTime |
| resolution = 0x32, // maps to Resolution |
| rangeOfInteger = 0x33, // maps to RangeOfInteger |
| collection = 0x34, // = begCollection tag [rfc8010], maps to Collection |
| textWithLanguage = 0x35, // maps to StringWithLanguage |
| nameWithLanguage = 0x36, // maps to StringWithLanguage |
| // 0x40-0x5f represents 'character-string values'. They map to std::string. |
| // All tags from the ranges 0x40-0x49 and 0x4b-0x5f are valid. |
| textWithoutLanguage = 0x41, |
| nameWithoutLanguage = 0x42, |
| keyword = 0x44, |
| uri = 0x45, |
| uriScheme = 0x46, |
| charset = 0x47, |
| naturalLanguage = 0x48, |
| mimeMediaType = 0x49 |
| // memberAttrName = 0x4a is invalid. |
| // 0x60-0xff are invalid. |
| }; |
| |
| // Is valid Out-of-Band tag (0x10-0x1f). |
| constexpr bool IsOutOfBand(ValueTag tag) { |
| return (tag >= static_cast<ValueTag>(0x10) && |
| tag <= static_cast<ValueTag>(0x1f)); |
| } |
| // Is valid integer type (0x21-0x23). |
| constexpr bool IsInteger(ValueTag tag) { |
| return (tag >= static_cast<ValueTag>(0x21) && |
| tag <= static_cast<ValueTag>(0x23)); |
| } |
| // Is valid character-string type (0x40-0x5f without 0x4a). |
| constexpr bool IsString(ValueTag tag) { |
| return (tag >= static_cast<ValueTag>(0x40) && |
| tag <= static_cast<ValueTag>(0x5f) && |
| tag != static_cast<ValueTag>(0x4a)); |
| } |
| // Is valid tag. |
| constexpr bool IsValid(ValueTag tag) { |
| return (IsOutOfBand(tag) || IsInteger(tag) || IsString(tag) || |
| (tag >= static_cast<ValueTag>(0x30) && |
| tag <= static_cast<ValueTag>(0x36))); |
| } |
| |
| // It is used to hold name and text values (see [rfc8010]). |
| // If language == "" it represents nameWithoutLanguage or textWithoutLanguage, |
| // otherwise it represents nameWithLanguage or textWithLanguage. |
| struct StringWithLanguage { |
| std::string value = ""; |
| std::string language = ""; |
| StringWithLanguage() = default; |
| StringWithLanguage(const std::string& value, const std::string& language) |
| : value(value), language(language) {} |
| explicit StringWithLanguage(const std::string& value) : value(value) {} |
| explicit StringWithLanguage(std::string&& value) : value(value) {} |
| operator std::string() const { return value; } |
| }; |
| inline bool operator==(const StringWithLanguage& v1, |
| const StringWithLanguage& v2) { |
| return (v1.language == v2.language) && (v1.value == v2.value); |
| } |
| inline bool operator!=(const StringWithLanguage& v1, |
| const StringWithLanguage& v2) { |
| return !(v1 == v2); |
| } |
| |
| // Represents resolution type from [rfc8010]. |
| struct Resolution { |
| int32_t xres = 0; |
| int32_t yres = 0; |
| enum Units : int8_t { |
| kDotsPerInch = 3, |
| kDotsPerCentimeter = 4 |
| } units = kDotsPerInch; |
| Resolution() = default; |
| Resolution(int32_t size1, int32_t size2, Units units = Units::kDotsPerInch) |
| : xres(size1), yres(size2), units(units) {} |
| }; |
| inline bool operator==(const Resolution& v1, const Resolution& v2) { |
| return (v1.xres == v2.xres) && (v1.yres == v2.yres) && (v1.units == v2.units); |
| } |
| inline bool operator!=(const Resolution& v1, const Resolution& v2) { |
| return !(v1 == v2); |
| } |
| |
| // Represents rangeOfInteger type from [rfc8010]. |
| struct RangeOfInteger { |
| int32_t min_value = 0; |
| int32_t max_value = 0; |
| RangeOfInteger() = default; |
| RangeOfInteger(int32_t min_value, int32_t max_value) |
| : min_value(min_value), max_value(max_value) {} |
| }; |
| inline bool operator==(const RangeOfInteger& v1, const RangeOfInteger& v2) { |
| return (v1.min_value == v2.min_value) && (v1.max_value == v2.max_value); |
| } |
| inline bool operator!=(const RangeOfInteger& v1, const RangeOfInteger& v2) { |
| return !(v1 == v2); |
| } |
| |
| // Represents dateTime type from [rfc8010,rfc2579]. |
| struct DateTime { |
| uint16_t year = 2000; |
| uint8_t month = 1; // 1..12 |
| uint8_t day = 1; // 1..31 |
| uint8_t hour = 0; // 0..23 |
| uint8_t minutes = 0; // 0..59 |
| uint8_t seconds = 0; // 0..60 (60 - leap second :-) |
| uint8_t deci_seconds = 0; // 0..9 |
| uint8_t UTC_direction = '+'; // '+' / '-' |
| uint8_t UTC_hours = 0; // 0..13 |
| uint8_t UTC_minutes = 0; // 0..59 |
| }; |
| inline bool operator==(const DateTime& v1, const DateTime& v2) { |
| return (v1.year == v2.year) && (v1.month == v2.month) && (v1.day == v2.day) && |
| (v1.hour == v2.hour) && (v1.minutes == v2.minutes) && |
| (v1.seconds == v2.seconds) && (v1.deci_seconds == v2.deci_seconds) && |
| (v1.UTC_direction == v2.UTC_direction) && |
| (v1.UTC_hours == v2.UTC_hours) && (v1.UTC_minutes == v2.UTC_minutes); |
| } |
| inline bool operator!=(const DateTime& v1, const DateTime& v2) { |
| return !(v1 == v2); |
| } |
| |
| // Functions converting basic types to string. For enums it returns empty |
| // string if given value is not defined. |
| LIBIPP_EXPORT std::string_view ToStrView(ValueTag tag); |
| LIBIPP_EXPORT std::string ToString(bool value); |
| LIBIPP_EXPORT std::string ToString(int value); |
| LIBIPP_EXPORT std::string ToString(const Resolution& value); |
| LIBIPP_EXPORT std::string ToString(const RangeOfInteger& value); |
| LIBIPP_EXPORT std::string ToString(const DateTime& value); |
| LIBIPP_EXPORT std::string ToString(const StringWithLanguage& value); |
| |
| // Functions extracting basic types from string. |
| // Returns false <=> given pointer is nullptr or given string does not |
| // represent a correct value. |
| LIBIPP_EXPORT bool FromString(const std::string& str, bool* value); |
| LIBIPP_EXPORT bool FromString(const std::string& str, int* value); |
| |
| // Basic values are stored in attributes as variables of the following types: |
| enum InternalType : uint8_t { |
| kInteger, // int32_t |
| kString, // std::string |
| kStringWithLanguage, // ipp::StringWithLanguage |
| kResolution, // ipp::Resolution |
| kRangeOfInteger, // ipp::RangeOfInteger |
| kDateTime, // ipp::DateTime |
| kCollection // Collection* |
| }; |
| |
| class Attribute; |
| class Collection; |
| |
| // Helper structure |
| struct AttrDef { |
| ValueTag ipp_type; |
| InternalType cc_type; |
| }; |
| |
| // Base class for all IPP collections. Collections is like struct filled with |
| // Attributes. Each attribute in Collection must have unique non-empty name. |
| // Use AddAttr() methods to add new attributes to the collection and GetAttr() |
| // to get access to the attribute by its name. To iterate over all attributes in |
| // the collection use iterators, e.g.: |
| // |
| // for (Attribute& attr: collection) { ... } |
| // for (const Attribute& attr: collection) { ... } |
| // |
| // Attributes inside the collection are always in the same order they were added |
| // to it. They will also appear in the same order in the resultant frame. |
| class LIBIPP_EXPORT Collection { |
| public: |
| class const_iterator; |
| class iterator { |
| public: |
| using iterator_category = std::bidirectional_iterator_tag; |
| using value_type = Attribute; |
| using difference_type = int; |
| using pointer = Attribute*; |
| using reference = Attribute&; |
| |
| iterator() = default; |
| iterator& operator++() { |
| ++iter_; |
| return *this; |
| } |
| iterator& operator--() { |
| --iter_; |
| return *this; |
| } |
| iterator operator++(int) { return iterator(iter_++); } |
| iterator operator--(int) { return iterator(iter_--); } |
| Attribute& operator*() { return *(iter_->get()); } |
| Attribute* operator->() { return iter_->get(); } |
| bool operator==(const iterator& i) const { return iter_ == i.iter_; } |
| bool operator!=(const iterator& i) const { return iter_ != i.iter_; } |
| bool operator==(const const_iterator& i) const { return iter_ == i.iter_; } |
| bool operator!=(const const_iterator& i) const { return iter_ != i.iter_; } |
| |
| private: |
| friend class Collection; |
| explicit iterator(std::vector<std::unique_ptr<Attribute>>::iterator iter) |
| : iter_(iter) {} |
| std::vector<std::unique_ptr<Attribute>>::iterator iter_; |
| }; |
| |
| class const_iterator { |
| public: |
| using iterator_category = std::bidirectional_iterator_tag; |
| using value_type = const Attribute; |
| using difference_type = int; |
| using pointer = const Attribute*; |
| using reference = const Attribute&; |
| |
| const_iterator() = default; |
| explicit const_iterator(iterator it) : iter_(it.iter_) {} |
| const_iterator& operator=(iterator it) { |
| iter_ = it.iter_; |
| return *this; |
| } |
| const_iterator& operator++() { |
| ++iter_; |
| return *this; |
| } |
| const_iterator& operator--() { |
| --iter_; |
| return *this; |
| } |
| const_iterator operator++(int) { return const_iterator(iter_++); } |
| const_iterator operator--(int) { return const_iterator(iter_--); } |
| const Attribute& operator*() { return *(iter_->get()); } |
| const Attribute* operator->() { return iter_->get(); } |
| bool operator==(const iterator& i) const { return iter_ == i.iter_; } |
| bool operator!=(const iterator& i) const { return iter_ != i.iter_; } |
| bool operator==(const const_iterator& i) const { return iter_ == i.iter_; } |
| bool operator!=(const const_iterator& i) const { return iter_ != i.iter_; } |
| |
| private: |
| friend class Collection; |
| explicit const_iterator( |
| std::vector<std::unique_ptr<Attribute>>::const_iterator iter) |
| : iter_(iter) {} |
| std::vector<std::unique_ptr<Attribute>>::const_iterator iter_; |
| }; |
| |
| Collection(); |
| Collection(const Collection&) = delete; |
| Collection(Collection&&) = delete; |
| Collection& operator=(const Collection&) = delete; |
| Collection& operator=(Collection&&) = delete; |
| virtual ~Collection(); |
| |
| // Standard container methods. |
| iterator begin() { return iterator(attributes_.begin()); } |
| iterator end() { return iterator(attributes_.end()); } |
| const_iterator cbegin() const { return const_iterator(attributes_.cbegin()); } |
| const_iterator cend() const { return const_iterator(attributes_.cend()); } |
| const_iterator begin() const { return cbegin(); } |
| const_iterator end() const { return cend(); } |
| size_t size() const { return attributes_index_.size(); } |
| bool empty() const { return attributes_index_.empty(); } |
| |
| // Methods return attribute by name. Methods return an iterator end() <=> the |
| // collection has no attributes with this name. |
| iterator GetAttr(std::string_view name); |
| const_iterator GetAttr(std::string_view name) const; |
| |
| // Add a new attribute without values. `tag` must be Out-Of-Band (see ValueTag |
| // definition). Possible errors: |
| // * kInvalidName |
| // * kNameConflict |
| // * kInvalidValueTag |
| // * kIncompatibleType (`tag` is not Out-Of-Band) |
| // * kTooManyAttributes. |
| Code AddAttr(const std::string& name, ValueTag tag); |
| |
| // Add a new attribute with one or more values. `tag` must be compatible with |
| // type of the parameter `value`/`values` according to the following rules: |
| // * int32_t: IsInteger(tag) == true |
| // * std::string: IsString(tag) == true OR tag == octetString |
| // * StringWithLanguage: tag == nameWithLanguage OR tag == textWithLanguage |
| // * DateTime: tag == dateTime |
| // * Resolution: tag == resolution |
| // * RangeOfInteger: tag == rangeOfInteger |
| // Possible errors: |
| // * kInvalidName |
| // * kNameConflict |
| // * kInvalidValueTag |
| // * kIncompatibleType (see the rules above) |
| // * kValueOutOfRange (the vector is empty or one of the value is invalid) |
| // * kTooManyAttributes. |
| Code AddAttr(const std::string& name, ValueTag tag, int32_t value); |
| Code AddAttr(const std::string& name, ValueTag tag, const std::string& value); |
| Code AddAttr(const std::string& name, |
| ValueTag tag, |
| const StringWithLanguage& value); |
| Code AddAttr(const std::string& name, ValueTag tag, DateTime value); |
| Code AddAttr(const std::string& name, ValueTag tag, Resolution value); |
| Code AddAttr(const std::string& name, ValueTag tag, RangeOfInteger value); |
| Code AddAttr(const std::string& name, |
| ValueTag tag, |
| const std::vector<int32_t>& values); |
| Code AddAttr(const std::string& name, |
| ValueTag tag, |
| const std::vector<std::string>& values); |
| Code AddAttr(const std::string& name, |
| ValueTag tag, |
| const std::vector<StringWithLanguage>& values); |
| Code AddAttr(const std::string& name, |
| ValueTag tag, |
| const std::vector<DateTime>& values); |
| Code AddAttr(const std::string& name, |
| ValueTag tag, |
| const std::vector<Resolution>& values); |
| Code AddAttr(const std::string& name, |
| ValueTag tag, |
| const std::vector<RangeOfInteger>& values); |
| |
| // Add a new attribute with one or more values. Tag of the new attribute is |
| // deduced from the type of the parameter `value`/`values`. |
| // Possible errors: |
| // * kInvalidName |
| // * kNameConflict |
| // * kValueOutOfRange (the vector is empty) |
| // * kTooManyAttributes. |
| Code AddAttr(const std::string& name, bool value); |
| Code AddAttr(const std::string& name, int32_t value); |
| Code AddAttr(const std::string& name, DateTime value); |
| Code AddAttr(const std::string& name, Resolution value); |
| Code AddAttr(const std::string& name, RangeOfInteger value); |
| Code AddAttr(const std::string& name, const std::vector<bool>& values); |
| Code AddAttr(const std::string& name, const std::vector<int32_t>& values); |
| Code AddAttr(const std::string& name, const std::vector<DateTime>& values); |
| Code AddAttr(const std::string& name, const std::vector<Resolution>& values); |
| Code AddAttr(const std::string& name, |
| const std::vector<RangeOfInteger>& values); |
| |
| // Add a new attribute with one or more collections. The first method creates |
| // an attribute with a single collection and returns an iterator to it in the |
| // last parameter. The second method creates an attribute with `size` |
| // collections and returns a view of them in the last parameter. |
| // Possible errors: |
| // * kInvalidName |
| // * kNameConflict |
| // * kValueOutOfRange (`size` is out of range) |
| // * kTooManyAttributes. |
| Code AddAttr(const std::string& name, CollsView::iterator& coll); |
| Code AddAttr(const std::string& name, size_t size, CollsView& colls); |
| |
| private: |
| friend class Attribute; |
| |
| // Adds new attribute to the collection. Returns Code::OK <=> an attribute |
| // was created. A pointer to the new attribute is saved to `new_attr`. |
| Code CreateNewAttribute(const std::string& name, |
| ValueTag type, |
| Attribute*& new_attr); |
| |
| // Tries to add a new attribute to the collection and set initial values for |
| // it. This function does not check compatibility of `tag` and ApiType. All |
| // other constraints are enforced. If `tag` is Out-Of-Band the parameter |
| // `values` is ignored. |
| template <typename ApiType> |
| Code AddAttributeToCollection(const std::string& name, |
| ValueTag tag, |
| const std::vector<ApiType>& values); |
| |
| // Stores attributes in the order they are saved in the frame. |
| std::vector<std::unique_ptr<Attribute>> attributes_; |
| |
| // Indexes attributes by name. Values are indices from `attributes_`. |
| std::unordered_map<std::string_view, size_t> attributes_index_; |
| }; |
| |
| // Base class representing Attribute, contains general API for Attribute. |
| class LIBIPP_EXPORT Attribute { |
| public: |
| Attribute(const Attribute&) = delete; |
| Attribute(Attribute&&) = delete; |
| Attribute& operator=(const Attribute&) = delete; |
| Attribute& operator=(Attribute&&) = delete; |
| |
| virtual ~Attribute(); |
| |
| // Returns tag of the attribute. |
| ValueTag Tag() const; |
| |
| // Returns an attribute's name. It is always a non-empty string. |
| std::string_view Name() const; |
| |
| // Returns the current number of elements (values or Collections). |
| // It returns 0 <=> IsOutOfBand(Tag()). |
| size_t Size() const; |
| |
| // Resizes the attribute (changes the number of stored values/collections). |
| // When (IsOutOfBand(Tag()) or `new_size` equals 0 this method does nothing. |
| void Resize(size_t new_size); |
| |
| // Retrieves a single value from the attribute and saves it in `value`. |
| // Possible errors: |
| // * kIncompatibleType |
| // * kIndexOutOfRange |
| // The second parameter must match the type of the attribute, otherwise the |
| // code kIncompatibleType is returned. However, there are a couple of |
| // exceptions when the underlying value is silently converted to the type of |
| // the given parameter. See the table below for a list of silent conversions: |
| // |
| // ValueTag | target C++ type |
| // -------------------+-------------------- |
| // boolean | int32_t (0 or 1) |
| // enum | int32_t |
| // integer | RangeOfIntegers (as range [int,int]) |
| // nameWithoutLanguage | StringWithLanguage (language is empty) |
| // textWithoutLanguage | StringWithLanguage (language is empty) |
| // |
| Code GetValue(size_t index, bool& value) const; |
| Code GetValue(size_t index, int32_t& value) const; |
| Code GetValue(size_t index, std::string& value) const; |
| Code GetValue(size_t index, StringWithLanguage& value) const; |
| Code GetValue(size_t index, DateTime& value) const; |
| Code GetValue(size_t index, Resolution& value) const; |
| Code GetValue(size_t index, RangeOfInteger& value) const; |
| |
| // Retrieves values from the attribute. They are copied to the given vector. |
| // Possible errors: |
| // * kIncompatibleType |
| // These methods follow the same rules for types' conversions as GetValue(). |
| Code GetValues(std::vector<bool>& values) const; |
| Code GetValues(std::vector<int32_t>& values) const; |
| Code GetValues(std::vector<std::string>& values) const; |
| Code GetValues(std::vector<StringWithLanguage>& values) const; |
| Code GetValues(std::vector<DateTime>& values) const; |
| Code GetValues(std::vector<Resolution>& values) const; |
| Code GetValues(std::vector<RangeOfInteger>& values) const; |
| |
| // Set new values for the attribute. Previous values are discarded. |
| // Possible errors: |
| // * kIncompatibleType |
| // * kValueOutOfRange |
| Code SetValues(bool value); |
| Code SetValues(int32_t value); |
| Code SetValues(const std::string& value); |
| Code SetValues(const StringWithLanguage& value); |
| Code SetValues(DateTime value); |
| Code SetValues(Resolution value); |
| Code SetValues(RangeOfInteger value); |
| Code SetValues(const std::vector<bool>& values); |
| Code SetValues(const std::vector<int32_t>& values); |
| Code SetValues(const std::vector<std::string>& values); |
| Code SetValues(const std::vector<StringWithLanguage>& values); |
| Code SetValues(const std::vector<DateTime>& values); |
| Code SetValues(const std::vector<Resolution>& values); |
| Code SetValues(const std::vector<RangeOfInteger>& values); |
| |
| // Provides access to Collection objects. You can iterate over them in the |
| // following ways: |
| // for (Collection& coll: attr.Colls()) { |
| // ... |
| // } |
| // or |
| // for (size_t i = 0; i < attr.Colls().size(); ) { |
| // Collection& coll = attr.Colls()[i++]; |
| // ... |
| // } |
| // (Tag() != collection) <=> attr.Colls().empty() |
| CollsView Colls(); |
| ConstCollsView Colls() const; |
| |
| private: |
| friend class Collection; |
| |
| // Constructor is called from Collection only. |
| Attribute(std::string_view name, AttrDef def); |
| |
| // Returns the current number of elements (values or Collections). |
| // (IsASet() == false) => always returns 0 or 1. |
| size_t GetSize() const; |
| |
| // Helper template function. |
| template <typename ApiType> |
| bool SaveValue(size_t index, const ApiType& value); |
| |
| // The name of the attribute. |
| std::string name_; |
| |
| // Defines the type of values stored in the attribute. |
| const AttrDef def_; |
| |
| // Stores values of the attribute. |
| void* values_ = nullptr; |
| }; |
| |
| } // namespace ipp |
| |
| #endif // LIBIPP_IPP_ATTRIBUTE_H_ |