You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
260 lines
6.5 KiB
260 lines
6.5 KiB
/* |
|
* Copyright (C) 2011-2012 Daniel Scharrer |
|
* |
|
* This software is provided 'as-is', without any express or implied |
|
* warranty. In no event will the author(s) be held liable for any damages |
|
* arising from the use of this software. |
|
* |
|
* Permission is granted to anyone to use this software for any purpose, |
|
* including commercial applications, and to alter it and redistribute it |
|
* freely, subject to the following restrictions: |
|
* |
|
* 1. The origin of this software must not be misrepresented; you must not |
|
* claim that you wrote the original software. If you use this software |
|
* in a product, an acknowledgment in the product documentation would be |
|
* appreciated but is not required. |
|
* 2. Altered source versions must be plainly marked as such, and must not be |
|
* misrepresented as being the original software. |
|
* 3. This notice may not be removed or altered from any source distribution. |
|
*/ |
|
|
|
#ifndef INNOEXTRACT_UTIL_STOREDENUM_HPP |
|
#define INNOEXTRACT_UTIL_STOREDENUM_HPP |
|
|
|
#include <stdint.h> |
|
#include <stddef.h> |
|
#include <vector> |
|
|
|
#include <boost/utility/enable_if.hpp> |
|
#include <boost/static_assert.hpp> |
|
|
|
#include "util/enum.hpp" |
|
#include "util/load.hpp" |
|
#include "util/log.hpp" |
|
#include "util/util.hpp" |
|
|
|
template <class Enum> |
|
struct enum_value_map { |
|
|
|
typedef Enum enum_type; |
|
typedef Enum flag_type; |
|
|
|
}; |
|
|
|
#define STORED_ENUM_MAP(MapName, Default, ...) \ |
|
struct MapName : public enum_value_map<typeof(Default)> { \ |
|
static const flag_type default_value; \ |
|
static const flag_type values[]; \ |
|
static const size_t count; \ |
|
}; \ |
|
const MapName::flag_type MapName::default_value = Default; \ |
|
const MapName::flag_type MapName::values[] = { __VA_ARGS__ }; \ |
|
const size_t MapName::count = ARRAY_SIZE(MapName::values) |
|
|
|
#define STORED_FLAGS_MAP(MapName, Flag0, ...) \ |
|
STORED_ENUM_MAP(MapName, Flag0, Flag0, ## __VA_ARGS__) |
|
|
|
template <class Mapping> |
|
struct stored_enum { |
|
|
|
size_t value; |
|
|
|
public: |
|
|
|
typedef Mapping mapping_type; |
|
typedef typename Mapping::enum_type enum_type; |
|
|
|
static const size_t size = Mapping::count; |
|
|
|
inline stored_enum(std::istream & is) { |
|
BOOST_STATIC_ASSERT(size <= (1 << 8)); |
|
value = load_number<uint8_t>(is); |
|
} |
|
|
|
enum_type get() { |
|
|
|
if(value < size) { |
|
return Mapping::values[value]; |
|
} |
|
|
|
log_warning << "unexpected " << enum_names<enum_type>::name << " value: " << value; |
|
|
|
return Mapping::default_value; |
|
} |
|
|
|
}; |
|
|
|
/*! |
|
* Load a packed bitfield: 1 byte for every 8 bits |
|
* The only exception is that 3-byte bitfields are padded to 4 bytes for non-16-bit builds. |
|
*/ |
|
template <size_t Bits, size_t PadBits = 32> |
|
class stored_bitfield { |
|
|
|
typedef uint8_t base_type; |
|
|
|
static const size_t base_size = sizeof(base_type) * 8; |
|
static const size_t count = (Bits + (base_size - 1)) / base_size; // ceildiv |
|
|
|
base_type bits[count]; |
|
|
|
public: |
|
|
|
static const size_t size = Bits; |
|
|
|
inline stored_bitfield(std::istream & is) { |
|
for(size_t i = 0; i < count; i++) { |
|
bits[i] = load_number<base_type>(is); |
|
} |
|
if(count == 3 && PadBits == 32) { |
|
// 3-byte sets are padded to 4 bytes |
|
(void)load_number<base_type>(is); |
|
} |
|
} |
|
|
|
inline uint64_t lower_bits() const { |
|
|
|
BOOST_STATIC_ASSERT(sizeof(uint64_t) % sizeof(base_type) == 0); |
|
|
|
uint64_t result = 0; |
|
|
|
for(size_t i = 0; i < std::min(sizeof(uint64_t) / sizeof(base_type), size_t(count)); i++) { |
|
result |= (uint64_t(bits[i]) << (i * base_size)); |
|
} |
|
|
|
return result; |
|
} |
|
|
|
inline operator std::bitset<size>() const { |
|
|
|
// Make `make style` shut up since we really need unsigned long here. |
|
#define stored_enum_concat_(a, b, c, d) a##b c##d |
|
typedef stored_enum_concat_(unsi, gned, lo, ng) ulong_type; |
|
#undef stored_enum_concat_ |
|
|
|
static const size_t ulong_size = sizeof(ulong_type) * 8; |
|
|
|
BOOST_STATIC_ASSERT(base_size % ulong_size == 0 || base_size < ulong_size); |
|
|
|
std::bitset<size> result(0); |
|
for(size_t i = 0; i < count; i++) { |
|
for(size_t j = 0; j < ceildiv(base_size, ulong_size); j++) { |
|
result |= std::bitset<size>(static_cast<ulong_type>(bits[i] >> (j * ulong_size))) |
|
<< ((i * base_size) + (j * ulong_size)); |
|
} |
|
} |
|
return result; |
|
} |
|
|
|
}; |
|
|
|
/*! |
|
* Load a flag set where the possible flags are known at compile-time. |
|
* Inno Setup stores flag sets as packed bitfields: 1 byte for every 8 flags |
|
* The only exception is that 3-byte bitfields are padded to 4 bytes for non-16-bit builds. |
|
*/ |
|
template <class Mapping, size_t PadBits = 32> |
|
class stored_flags : private stored_bitfield<Mapping::count, PadBits> { |
|
|
|
public: |
|
|
|
typedef Mapping mapping_type; |
|
typedef typename Mapping::enum_type enum_type; |
|
typedef flags<enum_type> flag_type; |
|
|
|
inline stored_flags(std::istream & is) : stored_bitfield<Mapping::count, PadBits>(is) { } |
|
|
|
flag_type get() { |
|
|
|
uint64_t bits = this->lower_bits(); |
|
flag_type result = 0; |
|
|
|
for(size_t i = 0; i < this->size; i++) { |
|
if(bits & (uint64_t(1) << i)) { |
|
result |= Mapping::values[i]; |
|
bits &= ~(uint64_t(1) << i); |
|
} |
|
} |
|
|
|
if(bits) { |
|
log_warning << "unexpected " << enum_names<enum_type>::name << " flags: " |
|
<< std::hex << bits << std::dec; |
|
} |
|
|
|
return result; |
|
} |
|
|
|
}; |
|
|
|
/*! |
|
* Load a flag set where the possible flags are not known at compile-time. |
|
* Inno Setup stores flag sets as packed bitfields: 1 byte for every 8 flags |
|
* The only exception is that 3-byte bitfields are padded to 4 bytes for non-16-bit builds. |
|
*/ |
|
template <class Enum> |
|
class stored_flag_reader { |
|
|
|
public: |
|
|
|
typedef Enum enum_type; |
|
typedef flags<enum_type> flag_type; |
|
|
|
private: |
|
|
|
const size_t pad_bits; |
|
|
|
std::istream & is; |
|
|
|
typedef uint8_t stored_type; |
|
static const size_t stored_bits = sizeof(stored_type) * 8; |
|
|
|
size_t pos; |
|
stored_type buffer; |
|
|
|
flag_type result; |
|
|
|
size_t bytes; |
|
|
|
public: |
|
|
|
explicit stored_flag_reader(std::istream & _is, size_t pad_bits = 32) |
|
: pad_bits(pad_bits), is(_is), pos(0), result(0), bytes(0) { } |
|
|
|
//! Declare the next possible flag. |
|
void add(enum_type flag) { |
|
|
|
if(pos == 0) { |
|
bytes++; |
|
buffer = load_number<stored_type>(is); |
|
} |
|
|
|
if(buffer & (stored_type(1) << pos)) { |
|
result |= flag; |
|
} |
|
|
|
pos = (pos + 1) % stored_bits; |
|
} |
|
|
|
operator flag_type() const { |
|
if(bytes == 3 && pad_bits == 32) { |
|
// 3-byte sets are padded to 4 bytes |
|
(void)load_number<stored_type>(is); |
|
} |
|
return result; |
|
} |
|
|
|
}; |
|
|
|
template <class Enum> |
|
class stored_flag_reader<flags<Enum> > : public stored_flag_reader<Enum> { |
|
|
|
public: |
|
|
|
explicit stored_flag_reader(std::istream & is, size_t pad_bits = 32) |
|
: stored_flag_reader<Enum>(is, pad_bits) { } |
|
|
|
}; |
|
|
|
typedef stored_bitfield<256> stored_char_set; |
|
|
|
#endif // INNOEXTRACT_UTIL_STOREDENUM_HPP
|
|
|