mirror of
				https://github.com/Icinga/icinga2.git
				synced 2025-10-31 11:14:10 +01:00 
			
		
		
		
	Merge pull request #6489 from Icinga/feature/object-packer
Implement object packer for consistent hashing
This commit is contained in:
		
						commit
						b16d96d197
					
				| @ -57,6 +57,7 @@ set(base_SOURCES | ||||
|   number.cpp number.hpp number-script.cpp | ||||
|   object.cpp object.hpp object-script.cpp | ||||
|   objectlock.cpp objectlock.hpp | ||||
|   object-packer.cpp object-packer.hpp | ||||
|   objecttype.cpp objecttype.hpp | ||||
|   perfdatavalue.cpp perfdatavalue.hpp perfdatavalue-ti.hpp | ||||
|   primitivetype.cpp primitivetype.hpp | ||||
|  | ||||
							
								
								
									
										268
									
								
								lib/base/object-packer.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										268
									
								
								lib/base/object-packer.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,268 @@ | ||||
| /******************************************************************************
 | ||||
|  * Icinga 2                                                                   * | ||||
|  * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/)  *
 | ||||
|  *                                                                            * | ||||
|  * This program is free software; you can redistribute it and/or              * | ||||
|  * modify it under the terms of the GNU General Public License                * | ||||
|  * as published by the Free Software Foundation; either version 2             * | ||||
|  * of the License, or (at your option) any later version.                     * | ||||
|  *                                                                            * | ||||
|  * This program is distributed in the hope that it will be useful,            * | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of             * | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the              * | ||||
|  * GNU General Public License for more details.                               * | ||||
|  *                                                                            * | ||||
|  * You should have received a copy of the GNU General Public License          * | ||||
|  * along with this program; if not, write to the Free Software Foundation     * | ||||
|  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.             * | ||||
|  ******************************************************************************/ | ||||
| 
 | ||||
| #include "base/object-packer.hpp" | ||||
| #include "base/debug.hpp" | ||||
| #include "base/dictionary.hpp" | ||||
| #include "base/array.hpp" | ||||
| #include "base/objectlock.hpp" | ||||
| #include <algorithm> | ||||
| #include <climits> | ||||
| #include <cstdint> | ||||
| #include <utility> | ||||
| #include <vector> | ||||
| 
 | ||||
| using namespace icinga; | ||||
| 
 | ||||
| // Just for the sake of code readability
 | ||||
| typedef std::vector<char> StringBuilder; | ||||
| 
 | ||||
| union EndiannessDetector | ||||
| { | ||||
| 	EndiannessDetector() | ||||
| 	{ | ||||
| 		i = 1; | ||||
| 	} | ||||
| 
 | ||||
| 	int i; | ||||
| 	char buf[sizeof(int)]; | ||||
| }; | ||||
| 
 | ||||
| static const EndiannessDetector l_EndiannessDetector; | ||||
| 
 | ||||
| // Assumption: The compiler will optimize (away) if/else statements using this.
 | ||||
| #define MACHINE_LITTLE_ENDIAN (l_EndiannessDetector.buf[0]) | ||||
| 
 | ||||
| static void PackAny(const Value& value, StringBuilder& builder); | ||||
| 
 | ||||
| /**
 | ||||
|  * std::swap() seems not to work | ||||
|  */ | ||||
| static inline void SwapBytes(char& a, char& b) | ||||
| { | ||||
| 	char c = a; | ||||
| 	a = b; | ||||
| 	b = c; | ||||
| } | ||||
| 
 | ||||
| #if CHAR_MIN != 0 | ||||
| union CharU2SConverter | ||||
| { | ||||
| 	CharU2SConverter() | ||||
| 	{ | ||||
| 		s = 0; | ||||
| 	} | ||||
| 
 | ||||
| 	unsigned char u; | ||||
| 	signed char s; | ||||
| }; | ||||
| #endif | ||||
| 
 | ||||
| /**
 | ||||
|  * Avoid implementation-defined overflows during unsigned to signed casts | ||||
|  */ | ||||
| static inline char UIntToByte(unsigned i) | ||||
| { | ||||
| #if CHAR_MIN == 0 | ||||
| 	return i; | ||||
| #else | ||||
| 	CharU2SConverter converter; | ||||
| 
 | ||||
| 	converter.u = i; | ||||
| 	return converter.s; | ||||
| #endif | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * Append the given int as big-endian 64-bit unsigned int | ||||
|  */ | ||||
| static inline void PackUInt64BE(uint_least64_t i, StringBuilder& builder) | ||||
| { | ||||
| 	char buf[8] = { | ||||
| 		UIntToByte(i >> 56u), | ||||
| 		UIntToByte((i >> 48u) & 255u), | ||||
| 		UIntToByte((i >> 40u) & 255u), | ||||
| 		UIntToByte((i >> 32u) & 255u), | ||||
| 		UIntToByte((i >> 24u) & 255u), | ||||
| 		UIntToByte((i >> 16u) & 255u), | ||||
| 		UIntToByte((i >> 8u) & 255u), | ||||
| 		UIntToByte(i & 255u) | ||||
| 	}; | ||||
| 
 | ||||
| 	builder.insert(builder.end(), (char*)buf, (char*)buf + 8); | ||||
| } | ||||
| 
 | ||||
| union Double2BytesConverter | ||||
| { | ||||
| 	Double2BytesConverter() | ||||
| 	{ | ||||
| 		buf[0] = 0; | ||||
| 		buf[1] = 0; | ||||
| 		buf[2] = 0; | ||||
| 		buf[3] = 0; | ||||
| 		buf[4] = 0; | ||||
| 		buf[5] = 0; | ||||
| 		buf[6] = 0; | ||||
| 		buf[7] = 0; | ||||
| 	} | ||||
| 
 | ||||
| 	double f; | ||||
| 	char buf[8]; | ||||
| }; | ||||
| 
 | ||||
| /**
 | ||||
|  * Append the given double as big-endian IEEE 754 binary64 | ||||
|  */ | ||||
| static inline void PackFloat64BE(double f, StringBuilder& builder) | ||||
| { | ||||
| 	Double2BytesConverter converter; | ||||
| 
 | ||||
| 	converter.f = f; | ||||
| 
 | ||||
| 	if (MACHINE_LITTLE_ENDIAN) { | ||||
| 		SwapBytes(converter.buf[0], converter.buf[7]); | ||||
| 		SwapBytes(converter.buf[1], converter.buf[6]); | ||||
| 		SwapBytes(converter.buf[2], converter.buf[5]); | ||||
| 		SwapBytes(converter.buf[3], converter.buf[4]); | ||||
| 	} | ||||
| 
 | ||||
| 	builder.insert(builder.end(), (char*)converter.buf, (char*)converter.buf + 8); | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * Append the given string's length (BE uint64) and the string itself | ||||
|  */ | ||||
| static inline void PackString(const String& string, StringBuilder& builder) | ||||
| { | ||||
| 	PackUInt64BE(string.GetLength(), builder); | ||||
| 	builder.insert(builder.end(), string.Begin(), string.End()); | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * Append the given array | ||||
|  */ | ||||
| static inline void PackArray(const Array::Ptr& arr, StringBuilder& builder) | ||||
| { | ||||
| 	ObjectLock olock(arr); | ||||
| 
 | ||||
| 	builder.emplace_back(5); | ||||
| 	PackUInt64BE(arr->GetLength(), builder); | ||||
| 
 | ||||
| 	for (const Value& value : arr) { | ||||
| 		PackAny(value, builder); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * Append the given dictionary | ||||
|  */ | ||||
| static inline void PackDictionary(const Dictionary::Ptr& dict, StringBuilder& builder) | ||||
| { | ||||
| 	ObjectLock olock(dict); | ||||
| 
 | ||||
| 	builder.emplace_back(6); | ||||
| 	PackUInt64BE(dict->GetLength(), builder); | ||||
| 
 | ||||
| 	for (const Dictionary::Pair& kv : dict) { | ||||
| 		PackString(kv.first, builder); | ||||
| 		PackAny(kv.second, builder); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * Append any JSON-encodable value | ||||
|  */ | ||||
| static void PackAny(const Value& value, StringBuilder& builder) | ||||
| { | ||||
| 	switch (value.GetType()) { | ||||
| 		case ValueString: | ||||
| 			builder.emplace_back(4); | ||||
| 			PackString(value.Get<String>(), builder); | ||||
| 			break; | ||||
| 
 | ||||
| 		case ValueNumber: | ||||
| 			builder.emplace_back(3); | ||||
| 			PackFloat64BE(value.Get<double>(), builder); | ||||
| 			break; | ||||
| 
 | ||||
| 		case ValueBoolean: | ||||
| 			builder.emplace_back(value.ToBool() ? 2 : 1); | ||||
| 			break; | ||||
| 
 | ||||
| 		case ValueEmpty: | ||||
| 			builder.emplace_back(0); | ||||
| 			break; | ||||
| 
 | ||||
| 		case ValueObject: | ||||
| 			{ | ||||
| 				const Object::Ptr& obj = value.Get<Object::Ptr>(); | ||||
| 
 | ||||
| 				Dictionary::Ptr dict = dynamic_pointer_cast<Dictionary>(obj); | ||||
| 				if (dict) { | ||||
| 					PackDictionary(dict, builder); | ||||
| 					break; | ||||
| 				} | ||||
| 
 | ||||
| 				Array::Ptr arr = dynamic_pointer_cast<Array>(obj); | ||||
| 				if (arr) { | ||||
| 					PackArray(arr, builder); | ||||
| 					break; | ||||
| 				} | ||||
| 			} | ||||
| 
 | ||||
| 			builder.emplace_back(0); | ||||
| 			break; | ||||
| 
 | ||||
| 		default: | ||||
| 			VERIFY(!"Invalid variant type."); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * Pack any JSON-encodable value to a BSON-similar structure suitable for consistent hashing | ||||
|  * | ||||
|  * Spec: | ||||
|  *   null: 0x00 | ||||
|  *   false: 0x01 | ||||
|  *   true: 0x02 | ||||
|  *   number: 0x03 (ieee754_binary64_bigendian)payload | ||||
|  *   string: 0x04 (uint64_bigendian)payload.length (char[])payload | ||||
|  *   array: 0x05 (uint64_bigendian)payload.length (any[])payload | ||||
|  *   object: 0x06 (uint64_bigendian)payload.length (keyvalue[])payload.sort() | ||||
|  * | ||||
|  *   any: null|false|true|number|string|array|object | ||||
|  *   keyvalue: (uint64_bigendian)key.length (char[])key (any)value | ||||
|  * | ||||
|  * Assumptions: | ||||
|  *   - double is IEEE 754 binary64 | ||||
|  *   - all int types (signed and unsigned) and all float types share the same endianness | ||||
|  *   - char is exactly 8 bits wide and one char is exactly one byte affected by the machine endianness | ||||
|  *   - all input strings, arrays and dictionaries are at most 2^64-1 long | ||||
|  * | ||||
|  * If not, this function will silently produce invalid results. | ||||
|  */ | ||||
| String icinga::PackObject(const Value& value) | ||||
| { | ||||
| 	StringBuilder builder; | ||||
| 	PackAny(value, builder); | ||||
| 
 | ||||
| 	String result; | ||||
| 	result.insert(result.End(), builder.begin(), builder.end()); | ||||
| 	return std::move(result); | ||||
| } | ||||
							
								
								
									
										35
									
								
								lib/base/object-packer.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								lib/base/object-packer.hpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,35 @@ | ||||
| /******************************************************************************
 | ||||
|  * Icinga 2                                                                   * | ||||
|  * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/)  *
 | ||||
|  *                                                                            * | ||||
|  * This program is free software; you can redistribute it and/or              * | ||||
|  * modify it under the terms of the GNU General Public License                * | ||||
|  * as published by the Free Software Foundation; either version 2             * | ||||
|  * of the License, or (at your option) any later version.                     * | ||||
|  *                                                                            * | ||||
|  * This program is distributed in the hope that it will be useful,            * | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of             * | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the              * | ||||
|  * GNU General Public License for more details.                               * | ||||
|  *                                                                            * | ||||
|  * You should have received a copy of the GNU General Public License          * | ||||
|  * along with this program; if not, write to the Free Software Foundation     * | ||||
|  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.             * | ||||
|  ******************************************************************************/ | ||||
| 
 | ||||
| #ifndef OBJECT_PACKER | ||||
| #define OBJECT_PACKER | ||||
| 
 | ||||
| #include "base/i2-base.hpp" | ||||
| 
 | ||||
| namespace icinga | ||||
| { | ||||
| 
 | ||||
| class String; | ||||
| class Value; | ||||
| 
 | ||||
| String PackObject(const Value& value); | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| #endif /* OBJECT_PACKER */ | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user