1// File Description
2/// \file Tag.inl
3/// \brief Inline implementations for the Tag class.
4//
5// Author: Derek Barnett
6
7#include "pbbam/Tag.h"
8#include <boost/numeric/conversion/cast.hpp>
9#include <iostream>
10
11namespace PacBio {
12namespace BAM {
13namespace internal {
14
15template<typename T>
16inline bool InAsciiRange(const T x)
17{ return (x >=33 && x <= 127); }
18
19struct AsciiConvertVisitor : public boost::static_visitor<char>
20{
21    // only valid for numeric types - maybe even more restrictive?
22    char operator() (const int8_t& x) const   { return Helper(x); }
23    char operator() (const uint8_t& x) const  { return Helper(x); }
24    char operator() (const int16_t& x) const  { return Helper(x); }
25    char operator() (const uint16_t& x) const { return Helper(x); }
26    char operator() (const int32_t& x) const  { return Helper(x); }
27    char operator() (const uint32_t& x) const { return Helper(x); }
28
29    // anything else always throws
30    template<typename T>
31    char operator()(const T&) const
32    { throw std::runtime_error{"conversion not supported"}; return 0; }
33
34private:
35    template<typename T>
36    char Helper(const T& x) const
37    {
38        if (!InAsciiRange(x))
39            throw std::runtime_error{"not valid ASCII"};
40        return static_cast<char>(x);
41    }
42};
43
44template<typename DesiredType>
45struct NumericConvertVisitor : public boost::static_visitor<DesiredType>
46{
47    // only valid for integral types
48    DesiredType operator() (const int8_t& x) const   { return boost::numeric_cast<DesiredType>(x); }
49    DesiredType operator() (const uint8_t& x) const  { return boost::numeric_cast<DesiredType>(x); }
50    DesiredType operator() (const int16_t& x) const  { return boost::numeric_cast<DesiredType>(x); }
51    DesiredType operator() (const uint16_t& x) const { return boost::numeric_cast<DesiredType>(x); }
52    DesiredType operator() (const int32_t& x) const  { return boost::numeric_cast<DesiredType>(x); }
53    DesiredType operator() (const uint32_t& x) const { return boost::numeric_cast<DesiredType>(x); }
54
55    // anything else always throws
56    template<typename T> DesiredType operator()(const T& t) const
57    {
58        const std::string from = typeid(t).name();
59        const std::string to   = typeid(DesiredType).name();
60        const std::string msg  = "conversion not supported: " + from + " -> " + to;
61        throw std::runtime_error(msg);
62        return 0;
63    }
64};
65
66using ToInt8ConvertVisitor   = NumericConvertVisitor<int8_t>;
67using ToUInt8ConvertVisitor  = NumericConvertVisitor<uint8_t>;
68using ToInt16ConvertVisitor  = NumericConvertVisitor<int16_t>;
69using ToUInt16ConvertVisitor = NumericConvertVisitor<uint16_t>;
70using ToInt32ConvertVisitor  = NumericConvertVisitor<int32_t>;
71using ToUInt32ConvertVisitor = NumericConvertVisitor<uint32_t>;
72
73struct IsEqualVisitor : public boost::static_visitor<bool>
74{
75    template <typename T, typename U>
76    bool operator() (const T&, const U&) const
77    {
78        // maybe allow conversions down the road?
79        // but for now, just fail if types are different
80        return false;
81    }
82
83    bool operator() (const boost::blank&, const boost::blank&) const
84    { return true; }
85
86    template <typename T>
87    bool operator() (const T& lhs, const T& rhs) const
88    { return lhs == rhs; }
89};
90
91struct TypenameVisitor : public boost::static_visitor<std::string>
92{
93    std::string operator() (const boost::blank&) const     { return "none"; }
94    std::string operator() (const int8_t&) const           { return "int8_t"; }
95    std::string operator() (const uint8_t&) const          { return "uint8_t"; }
96    std::string operator() (const int16_t&) const          { return "int16_t"; }
97    std::string operator() (const uint16_t&) const         { return "uint16_t"; }
98    std::string operator() (const int32_t&) const          { return "int32_t"; }
99    std::string operator() (const uint32_t&) const         { return "uint32_t"; }
100    std::string operator() (const float&) const            { return "float"; }
101    std::string operator() (const std::string&) const      { return "string"; }
102    std::string operator() (const std::vector<int8_t>&) const   { return "vector<int8_t>"; }
103    std::string operator() (const std::vector<uint8_t>&) const  { return "vector<uint8_t>"; }
104    std::string operator() (const std::vector<int16_t>&) const  { return "vector<int16_t>"; }
105    std::string operator() (const std::vector<uint16_t>&) const { return "vector<uint16_t>"; }
106    std::string operator() (const std::vector<int32_t>&) const  { return "vector<int32_t>"; }
107    std::string operator() (const std::vector<uint32_t>&) const { return "vector<uint32_t>"; }
108    std::string operator() (const std::vector<float>&) const    { return "vector<float>"; }
109};
110
111} // namespace internal
112
113inline Tag::Tag(int8_t value) : data_{value} {}
114inline Tag::Tag(uint8_t value) : data_{value} {}
115inline Tag::Tag(int16_t value) : data_{value} {}
116inline Tag::Tag(uint16_t value) : data_{value} {}
117inline Tag::Tag(int32_t value) : data_{value} {}
118inline Tag::Tag(uint32_t value) : data_{value} {}
119inline Tag::Tag(float value) : data_{value} {}
120inline Tag::Tag(std::string value) : data_{std::move(value)} {}
121inline Tag::Tag(std::vector<int8_t> value) : data_{std::move(value)} {}
122inline Tag::Tag(std::vector<uint8_t> value) : data_{std::move(value)} {}
123inline Tag::Tag(std::vector<int16_t> value) : data_{std::move(value)} {}
124inline Tag::Tag(std::vector<uint16_t> value) : data_{std::move(value)} {}
125inline Tag::Tag(std::vector<int32_t> value) : data_{std::move(value)} {}
126inline Tag::Tag(std::vector<uint32_t> value) : data_{std::move(value)} {}
127inline Tag::Tag(std::vector<float> value) : data_{std::move(value)} {}
128
129inline Tag::Tag(int8_t value, const TagModifier mod) : data_{value}, modifier_(mod)
130{
131    if (mod == TagModifier::HEX_STRING)
132        throw std::runtime_error{
133            "HEX_STRING is not a valid tag modifier for int8_t data. "
134            "It is intended for string-type data only."};
135}
136
137inline Tag::Tag(std::string value, TagModifier mod) : data_{std::move(value)}, modifier_{mod}
138{
139    if (mod == TagModifier::ASCII_CHAR)
140        throw std::runtime_error{
141            "ASCII_CHAR is not a valid tag modifier for string-type data. "
142            "To construct an ASCII char tag, use a single-quoted value (e.g. 'X' instead of "
143            "\"X\")"};
144}
145
146inline Tag& Tag::operator=(boost::blank value)
147{
148    data_ = value;
149    return *this;
150}
151
152inline Tag& Tag::operator=(int8_t value)
153{
154    data_ = value;
155    return *this;
156}
157
158inline Tag& Tag::operator=(uint8_t value)
159{
160    data_ = value;
161    return *this;
162}
163
164inline Tag& Tag::operator=(int16_t value)
165{
166    data_ = value;
167    return *this;
168}
169
170inline Tag& Tag::operator=(uint16_t value)
171{
172    data_ = value;
173    return *this;
174}
175
176inline Tag& Tag::operator=(int32_t value)
177{
178    data_ = value;
179    return *this;
180}
181
182inline Tag& Tag::operator=(uint32_t value)
183{
184    data_ = value;
185    return *this;
186}
187
188inline Tag& Tag::operator=(float value)
189{
190    data_ = value;
191    return *this;
192}
193
194inline Tag& Tag::operator=(std::string value)
195{
196    data_ = std::move(value);
197    return *this;
198}
199
200inline Tag& Tag::operator=(std::vector<int8_t> value)
201{
202    data_ = std::move(value);
203    return *this;
204}
205
206inline Tag& Tag::operator=(std::vector<uint8_t> value)
207{
208    data_ = std::move(value);
209    return *this;
210}
211
212inline Tag& Tag::operator=(std::vector<int16_t> value)
213{
214    data_ = std::move(value);
215    return *this;
216}
217
218inline Tag& Tag::operator=(std::vector<uint16_t> value)
219{
220    data_ = std::move(value);
221    return *this;
222}
223
224inline Tag& Tag::operator=(std::vector<int32_t> value)
225{
226    data_ = std::move(value);
227    return *this;
228}
229
230inline Tag& Tag::operator=(std::vector<uint32_t> value)
231{
232    data_ = std::move(value);
233    return *this;
234}
235
236inline Tag& Tag::operator=(std::vector<float> value)
237{
238    data_ = std::move(value);
239    return *this;
240}
241
242inline bool Tag::operator== (const Tag& other) const
243{
244    return boost::apply_visitor(internal::IsEqualVisitor(), data_, other.data_) &&
245           (modifier_ == other.modifier_) ;
246}
247
248inline bool Tag::operator!= (const Tag& other) const
249{ return !(*this == other); }
250
251inline bool Tag::HasModifier(const TagModifier m) const
252{
253    // we just allow one at a time (for now at least)
254    return modifier_ == m;
255}
256
257inline bool Tag::IsNull() const
258{ return Type() == TagDataType::INVALID; }
259
260inline bool Tag::IsInt8() const
261{ return Type() == TagDataType::INT8; }
262
263inline bool Tag::IsUInt8() const
264{ return Type() == TagDataType::UINT8; }
265
266inline bool Tag::IsInt16() const
267{ return Type() == TagDataType::INT16; }
268
269inline bool Tag::IsUInt16() const
270{ return Type() == TagDataType::UINT16; }
271
272inline bool Tag::IsInt32() const
273{ return Type() == TagDataType::INT32; }
274
275inline bool Tag::IsUInt32() const
276{ return Type() == TagDataType::UINT32; }
277
278inline bool Tag::IsFloat() const
279{ return Type() == TagDataType::FLOAT; }
280
281inline bool Tag::IsString() const
282{ return Type() == TagDataType::STRING; }
283
284inline bool Tag::IsHexString() const
285{ return IsString() && modifier_ == TagModifier::HEX_STRING; }
286
287inline bool Tag::IsInt8Array() const
288{ return Type() == TagDataType::INT8_ARRAY; }
289
290inline bool Tag::IsUInt8Array() const
291{ return Type() == TagDataType::UINT8_ARRAY; }
292
293inline bool Tag::IsInt16Array() const
294{ return Type() == TagDataType::INT16_ARRAY; }
295
296inline bool Tag::IsUInt16Array() const
297{ return Type() == TagDataType::UINT16_ARRAY; }
298
299inline bool Tag::IsInt32Array() const
300{ return Type() == TagDataType::INT32_ARRAY; }
301
302inline bool Tag::IsUInt32Array() const
303{ return Type() == TagDataType::UINT32_ARRAY; }
304
305inline bool Tag::IsFloatArray() const
306{ return Type() == TagDataType::FLOAT_ARRAY; }
307
308inline bool Tag::IsSignedInt() const
309{ return IsInt8() || IsInt16() || IsInt32(); }
310
311inline bool Tag::IsUnsignedInt() const
312{ return IsUInt8() || IsUInt16() || IsUInt32(); }
313
314inline bool Tag::IsIntegral() const
315{ return IsSignedInt() || IsUnsignedInt(); }
316
317inline bool Tag::IsNumeric() const
318{ return IsIntegral() || IsFloat(); }
319
320inline bool Tag::IsSignedArray() const
321{ return IsInt8Array() || IsInt16Array() || IsInt32Array(); }
322
323inline bool Tag::IsUnsignedArray() const
324{ return IsUInt8Array() || IsUInt16Array() || IsUInt32Array(); }
325
326inline bool Tag::IsIntegralArray() const
327{ return IsSignedArray() || IsUnsignedArray(); }
328
329inline bool Tag::IsArray() const
330{ return IsIntegralArray() || IsFloatArray(); }
331
332inline TagModifier Tag::Modifier() const
333{ return modifier_; }
334
335inline Tag& Tag::Modifier(const TagModifier m)
336{ modifier_ = m; return *this; }
337
338inline char Tag::ToAscii() const
339{ return boost::apply_visitor(internal::AsciiConvertVisitor(), data_); }
340
341inline int8_t Tag::ToInt8() const
342{
343    if (IsInt8())
344        return boost::get<int8_t>(data_);
345    return boost::apply_visitor(internal::ToInt8ConvertVisitor(), data_);
346}
347
348inline uint8_t Tag::ToUInt8() const
349{
350    if (IsUInt8())
351        return boost::get<uint8_t>(data_);
352    return boost::apply_visitor(internal::ToUInt8ConvertVisitor(), data_);
353}
354
355inline int16_t Tag::ToInt16() const
356{
357    if (IsInt16())
358        return boost::get<int16_t>(data_);
359    return boost::apply_visitor(internal::ToInt16ConvertVisitor(), data_);
360}
361
362inline uint16_t Tag::ToUInt16() const
363{
364    if (IsUInt16())
365        return boost::get<uint16_t>(data_);
366    return boost::apply_visitor(internal::ToUInt16ConvertVisitor(), data_);
367}
368
369inline int32_t Tag::ToInt32() const
370{
371    if (IsInt32())
372        return boost::get<int32_t>(data_);
373    return boost::apply_visitor(internal::ToInt32ConvertVisitor(), data_);
374}
375
376inline uint32_t Tag::ToUInt32() const
377{
378    if (IsUInt32())
379        return boost::get<uint32_t>(data_);
380    return boost::apply_visitor(internal::ToUInt32ConvertVisitor(), data_);
381}
382
383inline float Tag::ToFloat() const
384{ return boost::get<float>(data_); }
385
386inline std::string Tag::ToString() const
387{ return boost::get<std::string>(data_); }
388
389inline std::vector<int8_t> Tag::ToInt8Array() const
390{ return boost::get< std::vector<int8_t> >(data_); }
391
392inline std::vector<uint8_t> Tag::ToUInt8Array() const
393{ return boost::get< std::vector<uint8_t> >(data_); }
394
395inline std::vector<int16_t> Tag::ToInt16Array() const
396{ return boost::get< std::vector<int16_t> >(data_); }
397
398inline std::vector<uint16_t> Tag::ToUInt16Array() const
399{ return boost::get< std::vector<uint16_t> >(data_); }
400
401inline std::vector<int32_t> Tag::ToInt32Array() const
402{ return boost::get< std::vector<int32_t> >(data_); }
403
404inline std::vector<uint32_t> Tag::ToUInt32Array() const
405{ return boost::get< std::vector<uint32_t> >(data_); }
406
407inline std::vector<float> Tag::ToFloatArray() const
408{ return boost::get< std::vector<float> >(data_); }
409
410inline TagDataType Tag::Type() const
411{ return TagDataType(data_.which()  ); }
412
413inline std::string Tag::Typename() const
414{ return boost::apply_visitor(internal::TypenameVisitor(), data_); }
415
416} // namespace BAM
417} // namespace PacBio
418