blob: 4f03fc4c2a4202b0574fc1bc845e25999f469ad4 [file] [log] [blame]
#pragma once
#ifndef GEODE_GFCPP_DATAOUTPUT_H_
#define GEODE_GFCPP_DATAOUTPUT_H_
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "gfcpp_globals.hpp"
#include "ExceptionTypes.hpp"
#include "Log.hpp"
#include "Serializable.hpp"
#include "CacheableString.hpp"
extern "C" {
#include <string.h>
#include <stdlib.h>
}
/**
* @file
*/
namespace apache {
namespace geode {
namespace client {
/**
* C style memory allocation that throws OutOfMemoryException
* if it fails
*/
#define GF_ALLOC(v, t, s) \
{ \
v = (t*)malloc((s) * sizeof(t)); \
if ((v) == NULL) { \
throw OutOfMemoryException( \
"Out of Memory while allocating buffer for " #t " of size " #s); \
} \
}
/**
* C style memory re-allocation that throws OutOfMemoryException
* if it fails
*/
#define GF_RESIZE(v, t, s) \
{ \
v = (t*)realloc(v, (s) * sizeof(t)); \
if ((v) == NULL) { \
throw OutOfMemoryException( \
"Out of Memory while resizing buffer for " #t); \
} \
}
#define GF_FREE(v) free(v)
/**
* Provide operations for writing primitive data values, byte arrays,
* strings, <code>Serializable</code> objects to a byte stream.
* This class is intentionally not thread safe.
*/
class CPPCACHE_EXPORT DataOutput {
public:
/**
* Construct a new DataOutput.
*/
DataOutput();
/**
* Write an unsigned byte to the <code>DataOutput</code>.
*
* @param value the unsigned byte to be written
*/
inline void write(uint8_t value) {
ensureCapacity(1);
writeNoCheck(value);
}
/**
* Write a signed byte to the <code>DataOutput</code>.
*
* @param value the signed byte to be written
*/
inline void write(int8_t value) { write(static_cast<uint8_t>(value)); }
/**
* Write a boolean value to the <code>DataOutput</code>.
*
* @param value the boolean value to be written
*/
inline void writeBoolean(bool value) { write(static_cast<uint8_t>(value)); }
/**
* Write an array of unsigned bytes to the <code>DataOutput</code>.
*
* @param value the array of unsigned bytes to be written
* @param len the number of bytes from the start of array to be written
*/
inline void writeBytes(const uint8_t* bytes, int32_t len) {
if (len >= 0) {
ensureCapacity(len + 5);
writeArrayLen(bytes == NULL ? 0 : len); // length of bytes...
if (len > 0 && bytes != NULL) {
memcpy(m_buf, bytes, len);
m_buf += len;
}
} else {
write(static_cast<int8_t>(-1));
}
}
/**
* Write an array of signed bytes to the <code>DataOutput</code>.
*
* @param value the array of signed bytes to be written
* @param len the number of bytes from the start of array to be written
*/
inline void writeBytes(const int8_t* bytes, int32_t len) {
writeBytes(reinterpret_cast<const uint8_t*>(bytes), len);
}
/**
* Write an array of unsigned bytes without its length to the
* <code>DataOutput</code>.
* @remarks The difference between this and <code>writeBytes</code> is that
* this does write the length of bytes so the corresponding
* <code>DataInput::readBytesOnly</code> (unlike
* <code>DataInput::readBytes</code>) needs the length argument explicitly.
*
* @param value the array of unsigned bytes to be written
* @param len the number of bytes from the start of array to be written
*/
inline void writeBytesOnly(const uint8_t* bytes, uint32_t len) {
ensureCapacity(len);
memcpy(m_buf, bytes, len);
m_buf += len;
}
/**
* Write an array of signed bytes without its length to the
* <code>DataOutput</code>.
* @remarks The difference between this and <code>writeBytes</code> is that
* this does write the length of bytes so the corresponding
* <code>DataInput::readBytesOnly</code> (unlike
* <code>DataInput::readBytes</code>) needs the length argument explicitly.
*
* @param value the array of signed bytes to be written
* @param len the number of bytes from the start of array to be written
*/
inline void writeBytesOnly(const int8_t* bytes, uint32_t len) {
writeBytesOnly(reinterpret_cast<const uint8_t*>(bytes), len);
}
/**
* Write a 16-bit unsigned integer value to the <code>DataOutput</code>.
*
* @param value the 16-bit unsigned integer value to be written
*/
inline void writeInt(uint16_t value) {
ensureCapacity(2);
*(m_buf++) = static_cast<uint8_t>(value >> 8);
*(m_buf++) = static_cast<uint8_t>(value);
}
/**
* Write a 16-bit Char (wchar_t) value to the <code>DataOutput</code>.
*
* @param value the 16-bit wchar_t value to be written
*/
inline void writeChar(uint16_t value) {
ensureCapacity(2);
*(m_buf++) = static_cast<uint8_t>(value >> 8);
*(m_buf++) = static_cast<uint8_t>(value);
}
/**
* Write a 32-bit unsigned integer value to the <code>DataOutput</code>.
*
* @param value the 32-bit unsigned integer value to be written
*/
inline void writeInt(uint32_t value) {
ensureCapacity(4);
*(m_buf++) = static_cast<uint8_t>(value >> 24);
*(m_buf++) = static_cast<uint8_t>(value >> 16);
*(m_buf++) = static_cast<uint8_t>(value >> 8);
*(m_buf++) = static_cast<uint8_t>(value);
}
/**
* Write a 64-bit unsigned integer value to the <code>DataOutput</code>.
*
* @param value the 64-bit unsigned integer value to be written
*/
inline void writeInt(uint64_t value) {
ensureCapacity(8);
// the defines are not reliable and can be changed by compiler options.
// Hence using sizeof() test instead.
//#if defined(_LP64) || ( defined(__WORDSIZE) && __WORDSIZE == 64 ) ||
//( defined(_INTEGRAL_MAX_BITS) && _INTEGRAL_MAX_BITS >= 64 )
if (sizeof(long) == 8) {
*(m_buf++) = static_cast<uint8_t>(value >> 56);
*(m_buf++) = static_cast<uint8_t>(value >> 48);
*(m_buf++) = static_cast<uint8_t>(value >> 40);
*(m_buf++) = static_cast<uint8_t>(value >> 32);
*(m_buf++) = static_cast<uint8_t>(value >> 24);
*(m_buf++) = static_cast<uint8_t>(value >> 16);
*(m_buf++) = static_cast<uint8_t>(value >> 8);
*(m_buf++) = static_cast<uint8_t>(value);
} else {
uint32_t hword = static_cast<uint32_t>(value >> 32);
*(m_buf++) = static_cast<uint8_t>(hword >> 24);
*(m_buf++) = static_cast<uint8_t>(hword >> 16);
*(m_buf++) = static_cast<uint8_t>(hword >> 8);
*(m_buf++) = static_cast<uint8_t>(hword);
hword = static_cast<uint32_t>(value);
*(m_buf++) = static_cast<uint8_t>(hword >> 24);
*(m_buf++) = static_cast<uint8_t>(hword >> 16);
*(m_buf++) = static_cast<uint8_t>(hword >> 8);
*(m_buf++) = static_cast<uint8_t>(hword);
}
}
/**
* Write a 16-bit signed integer value to the <code>DataOutput</code>.
*
* @param value the 16-bit signed integer value to be written
*/
inline void writeInt(int16_t value) {
writeInt(static_cast<uint16_t>(value));
}
/**
* Write a 32-bit signed integer value to the <code>DataOutput</code>.
*
* @param value the 32-bit signed integer value to be written
*/
inline void writeInt(int32_t value) {
writeInt(static_cast<uint32_t>(value));
}
/**
* Write a 64-bit signed integer value to the <code>DataOutput</code>.
*
* @param value the 64-bit signed integer value to be written
*/
inline void writeInt(int64_t value) {
writeInt(static_cast<uint64_t>(value));
}
/**
* Write a 32-bit signed integer array length value to the
* <code>DataOutput</code> in a manner compatible with java server's
* <code>DataSerializer.writeArrayLength</code>.
*
* @param value the 32-bit signed integer array length to be written
*/
inline void writeArrayLen(int32_t len) {
if (len == -1) {
write(static_cast<int8_t>(-1));
} else if (len <= 252) { // 252 is java's ((byte)-4 && 0xFF)
write(static_cast<uint8_t>(len));
} else if (len <= 0xFFFF) {
write(static_cast<int8_t>(-2));
writeInt(static_cast<uint16_t>(len));
} else {
write(static_cast<int8_t>(-3));
writeInt(len);
}
}
/**
* Write a float value to the <code>DataOutput</code>.
*
* @param value the float value to be written
*/
inline void writeFloat(float value) {
union float_uint32_t {
float f;
uint32_t u;
} v;
v.f = value;
writeInt(v.u);
}
/**
* Write a double precision real number to the <code>DataOutput</code>.
*
* @param value the double precision real number to be written
*/
inline void writeDouble(double value) {
union double_uint64_t {
double d;
uint64_t ll;
} v;
v.d = value;
writeInt(v.ll);
}
/**
* Writes the given ASCII string supporting maximum length of 64K
* (i.e. unsigned 16-bit integer).
* @remarks The string will be truncated if greater than the maximum
* permissible length of 64K. Use <code>writeBytes</code> or
* <code>writeASCIIHuge</code> to write ASCII strings of length larger
* than this.
*
* @param value the C string to be written
* @param length the number of characters from start of string to be
* written; the default value of 0 implies the complete string
*/
inline void writeASCII(const char* value, uint32_t length = 0) {
if (value != NULL) {
if (length == 0) {
length = static_cast<uint32_t>(strlen(value));
}
uint16_t len = static_cast<uint16_t>(length > 0xFFFF ? 0xFFFF : length);
writeInt(len);
writeBytesOnly((int8_t*)value, len); // K64
} else {
writeInt(static_cast<uint16_t>(0));
}
}
inline void writeNativeString(const char* value) {
// create cacheable string
// write typeid id.
// call todata
CacheableStringPtr csPtr = CacheableString::create(value);
write(csPtr->typeId());
csPtr->toData(*this);
}
/**
* Writes the given ASCII string supporting upto maximum 32-bit
* integer value.
* @remarks Use this to write large ASCII strings. The other
* <code>writeASCII</code> method will truncate strings greater than
* 64K in size.
*
* @param value the wide-character string to be written
* @param length the number of characters from start of string to be
* written; the default value of 0 implies the complete string
*/
inline void writeASCIIHuge(const char* value, uint32_t length = 0) {
if (value != NULL) {
if (length == 0) {
length = static_cast<uint32_t>(strlen(value));
}
writeInt(length);
writeBytesOnly((int8_t*)value, length);
} else {
writeInt(static_cast<uint32_t>(0));
}
}
/**
* Writes the given given string using java modified UTF-8 encoding
* supporting maximum encoded length of 64K (i.e. unsigned 16-bit integer).
* @remarks The string will be truncated if greater than the maximum
* permissible length of 64K. Use <code>writeUTFHuge</code> to write
* strings of length larger than this.
*
* @param value the C string to be written
*
*/
inline void writeFullUTF(const char* value, uint32_t length = 0) {
if (value != NULL) {
int32_t len = getEncodedLength(value, length);
uint16_t encodedLen = static_cast<uint16_t>(len > 0xFFFF ? 0xFFFF : len);
writeInt(static_cast<int32_t>(encodedLen));
ensureCapacity(encodedLen);
write(static_cast<int8_t>(0)); // isObject = 0 BYTE_CODE
uint8_t* end = m_buf + encodedLen;
while (m_buf < end) {
encodeChar(*value++);
}
if (m_buf > end) m_buf = end;
} else {
writeInt(static_cast<uint16_t>(0));
}
}
/**
* Writes the given given string using java modified UTF-8 encoding
* supporting maximum encoded length of 64K (i.e. unsigned 16-bit integer).
* @remarks The string will be truncated if greater than the maximum
* permissible length of 64K. Use <code>writeUTFHuge</code> to write
* strings of length larger than this.
*
* @param value the C string to be written
* @param length the number of characters from start of string to be
* written; the default value of 0 implies the complete string
*/
inline void writeUTF(const char* value, uint32_t length = 0) {
if (value != NULL) {
int32_t len = getEncodedLength(value, length);
uint16_t encodedLen = static_cast<uint16_t>(len > 0xFFFF ? 0xFFFF : len);
writeInt(encodedLen);
ensureCapacity(encodedLen);
uint8_t* end = m_buf + encodedLen;
while (m_buf < end) {
encodeChar(*value++);
}
if (m_buf > end) m_buf = end;
} else {
writeInt(static_cast<uint16_t>(0));
}
}
/**
* Writes the given string using java modified UTF-8 encoding.
* @remarks Use this to write large strings. The other
* <code>writeUTF</code> method will truncate strings greater than
* 64K in size.
*
* @param value the C string to be written
* @param length the number of characters from start of string to be
* written; the default value of 0 implies the complete string
* assuming a null terminated string; do not use this unless sure
* that the UTF string does not contain any null characters
*/
inline void writeUTFHuge(const char* value, uint32_t length = 0) {
if (value != NULL) {
if (length == 0) {
length = static_cast<uint32_t>(strlen(value));
}
writeInt(length);
ensureCapacity(length * 2);
for (uint32_t pos = 0; pos < length; pos++) {
writeNoCheck(static_cast<int8_t>(0));
writeNoCheck(static_cast<int8_t>(value[pos]));
}
} else {
writeInt(static_cast<uint32_t>(0));
}
}
/**
* Writes the given given string using java modified UTF-8 encoding
* supporting maximum encoded length of 64K (i.e. unsigned 16-bit integer).
* @remarks The string will be truncated if greater than the maximum
* permissible length of 64K. Use <code>writeUTFHuge</code> to write
* strings of length larger than this.
*
* @param value the wide-character string to be written
* @param length the number of characters from start of string to be
* written; the default value of 0 implies the complete string
*/
inline void writeUTF(const wchar_t* value, uint32_t length = 0) {
if (value != NULL) {
int32_t len = getEncodedLength(value, length);
uint16_t encodedLen = static_cast<uint16_t>(len > 0xFFFF ? 0xFFFF : len);
writeInt(encodedLen);
ensureCapacity(encodedLen);
uint8_t* end = m_buf + encodedLen;
while (m_buf < end) {
encodeChar(*value++);
}
if (m_buf > end) m_buf = end;
} else {
writeInt(static_cast<uint16_t>(0));
}
}
/**
* Writes the given string using java modified UTF-8 encoding.
* @remarks Use this to write large strings. The other
* <code>writeUTF</code> method will truncate strings greater than
* 64K in size.
*
* @param value the wide-character string to be written
* @param length the number of characters from start of string to be
* written; the default value of 0 implies the complete string
*/
inline void writeUTFHuge(const wchar_t* value, uint32_t length = 0) {
if (value != NULL) {
if (length == 0) {
length = static_cast<uint32_t>(wcslen(value));
}
writeInt(length);
ensureCapacity(length * 2);
for (uint32_t pos = 0; pos < length; pos++) {
uint16_t item = static_cast<uint16_t>(value[pos]);
writeNoCheck(static_cast<uint8_t>((item & 0xFF00) >> 8));
writeNoCheck(static_cast<uint8_t>(item & 0xFF));
}
} else {
writeInt(static_cast<uint32_t>(0));
}
}
/**
* Get the length required to represent a given wide-character string in
* java modified UTF-8 format.
*
* @param value The C string.
* @param length The length of the string; or zero to use the full string.
* @return The length required for representation in java modified
* UTF-8 format.
* @see DataInput::getDecodedLength
*/
inline static int32_t getEncodedLength(const char* value, int32_t length = 0,
uint32_t* valLength = NULL) {
if (value == NULL) return 0;
char currentChar;
int32_t encodedLen = 0;
const char* start = value;
if (length == 0) {
while ((currentChar = *value) != '\0') {
getEncodedLength(currentChar, encodedLen);
value++;
}
} else {
const char* end = value + length;
while (value < end) {
currentChar = *value;
getEncodedLength(currentChar, encodedLen);
value++;
}
}
if (valLength != NULL) {
*valLength = static_cast<uint32_t>(value - start);
}
return encodedLen;
}
/**
* Get the length required to represent a given wide-character string in
* java modified UTF-8 format.
*
* @param value The wide-character string.
* @param length The length of the string.
* @return The length required for representation in java modified
* UTF-8 format.
* @see DataInput::getDecodedLength
*/
inline static int32_t getEncodedLength(const wchar_t* value,
int32_t length = 0,
uint32_t* valLength = NULL) {
if (value == NULL) return 0;
wchar_t currentChar;
int32_t encodedLen = 0;
const wchar_t* start = value;
if (length == 0) {
while ((currentChar = *value) != 0) {
getEncodedLength(currentChar, encodedLen);
value++;
}
} else {
const wchar_t* end = value + length;
while (value < end) {
currentChar = *value;
getEncodedLength(currentChar, encodedLen);
value++;
}
}
if (valLength != NULL) {
*valLength = static_cast<uint32_t>(value - start);
}
return encodedLen;
}
/**
* Write a <code>Serializable</code> object to the <code>DataOutput</code>.
*
* @param objptr smart pointer to the <code>Serializable</code> object
* to be written
*/
template <class PTR>
void writeObject(const SharedPtr<PTR>& objptr, bool isDelta = false) {
writeObjectInternal(objptr.ptr(), isDelta);
}
/**
* Write a <code>Serializable</code> object to the <code>DataOutput</code>.
*
* @param objptr pointer to the <code>Serializable</code> object
* to be written
*/
void writeObject(const Serializable* objptr) { writeObjectInternal(objptr); }
/**
* Get an internal pointer to the current location in the
* <code>DataOutput</code> byte array.
*/
const uint8_t* getCursor() { return m_buf; }
/**
* Advance the buffer cursor by the given offset.
*
* @param offset the offset by which to advance the cursor
*/
void advanceCursor(uint32_t offset) {
ensureCapacity(offset);
m_buf += offset;
}
/**
* Rewind the buffer cursor by the given offset.
*
* @param offset the offset by which to rewind the cursor
*/
void rewindCursor(uint32_t offset) { m_buf -= offset; }
void updateValueAtPos(uint32_t offset, uint8_t value) {
m_bytes[offset] = value;
}
uint8_t getValueAtPos(uint32_t offset) { return m_bytes[offset]; }
/** Destruct a DataOutput, including releasing the created buffer. */
~DataOutput() {
reset();
DataOutput::checkinBuffer(m_bytes, m_size);
}
/**
* Get a pointer to the internal buffer of <code>DataOutput</code>.
*/
inline const uint8_t* getBuffer() const {
// GF_R_ASSERT(!((uint32_t)(m_bytes) % 4));
return m_bytes;
}
/**
* Get a pointer to the internal buffer of <code>DataOutput</code>.
*/
inline uint32_t getRemainingBufferLength() const {
// GF_R_ASSERT(!((uint32_t)(m_bytes) % 4));
return m_size - getBufferLength();
}
/**
* Get a pointer to the internal buffer of <code>DataOutput</code>.
*
* @param rsize the size of buffer is filled in this output parameter;
* should not be NULL
*/
inline const uint8_t* getBuffer(uint32_t* rsize) const {
*rsize = static_cast<uint32_t>(m_buf - m_bytes);
// GF_R_ASSERT(!((uint32_t)(m_bytes) % 4));
return m_bytes;
}
inline uint8_t* getBufferCopy() {
uint32_t size = static_cast<uint32_t>(m_buf - m_bytes);
uint8_t* result;
GF_ALLOC(result, uint8_t, size);
memcpy(result, m_bytes, size);
return result;
}
/**
* Get the length of current data in the internal buffer of
* <code>DataOutput</code>.
*/
inline uint32_t getBufferLength() const {
return static_cast<uint32_t>(m_buf - m_bytes);
}
/**
* Reset the internal cursor to the start of the buffer.
*/
inline void reset() {
if (m_haveBigBuffer) {
// free existing buffer
GF_FREE(m_bytes);
// create smaller buffer
GF_ALLOC(m_bytes, uint8_t, m_lowWaterMark);
m_size = m_lowWaterMark;
// reset the flag
m_haveBigBuffer = false;
// release the lock
releaseLock();
}
m_buf = m_bytes;
}
// make sure there is room left for the requested size item.
inline void ensureCapacity(uint32_t size) {
uint32_t offset = static_cast<uint32_t>(m_buf - m_bytes);
if ((m_size - offset) < size) {
uint32_t newSize = m_size * 2 + (8192 * (size / 8192));
if (newSize >= m_highWaterMark && !m_haveBigBuffer) {
// acquire the lock
acquireLock();
// set flag
m_haveBigBuffer = true;
}
m_size = newSize;
GF_RESIZE(m_bytes, uint8_t, m_size);
m_buf = m_bytes + offset;
}
}
/*
* This is for internal use
*/
const char* getPoolName() { return m_poolName; }
/*
* This is for internal use
*/
void setPoolName(const char* poolName) { m_poolName = poolName; }
uint8_t* getBufferCopyFrom(const uint8_t* from, uint32_t length) {
uint8_t* result;
GF_NEW(result, uint8_t[length]);
memcpy(result, from, length);
return result;
}
static void safeDelete(uint8_t* src) { GF_SAFE_DELETE(src); }
static DataOutput* getDataOutput() { return new DataOutput(); }
static void releaseDataOutput(DataOutput* dataOutput) {
GF_SAFE_DELETE(dataOutput);
}
private:
void writeObjectInternal(const Serializable* ptr, bool isDelta = false);
static void acquireLock();
static void releaseLock();
const char* m_poolName;
// memory m_buffer to encode to.
uint8_t* m_bytes;
// cursor.
uint8_t* m_buf;
// size of m_bytes.
uint32_t m_size;
// high and low water marks for buffer size
static uint32_t m_lowWaterMark;
static uint32_t m_highWaterMark;
// flag to indicate we have a big buffer
volatile bool m_haveBigBuffer;
inline static void getEncodedLength(const char val, int32_t& encodedLen) {
if ((val == 0) || (val & 0x80)) {
// two byte.
encodedLen += 2;
} else {
// one byte.
encodedLen++;
}
}
inline static void getEncodedLength(const wchar_t val, int32_t& encodedLen) {
if (val == 0) {
encodedLen += 2;
} else if (val < 0x80) // ASCII character
{
encodedLen++;
} else if (val < 0x800) {
encodedLen += 2;
} else {
encodedLen += 3;
}
}
inline void encodeChar(const char value) {
uint8_t tmp = static_cast<uint8_t>(value);
if ((tmp == 0) || (tmp & 0x80)) {
// two byte.
*(m_buf++) = static_cast<uint8_t>(0xc0 | ((tmp & 0xc0) >> 6));
*(m_buf++) = static_cast<uint8_t>(0x80 | (tmp & 0x3f));
} else {
// one byte.
*(m_buf++) = tmp;
}
}
// this will lose the character set encoding.
inline void encodeChar(const wchar_t value) {
uint16_t c = static_cast<uint16_t>(value);
if (c == 0) {
*(m_buf++) = 0xc0;
*(m_buf++) = 0x80;
} else if (c < 0x80) { // ASCII character
*(m_buf++) = static_cast<uint8_t>(c);
} else if (c < 0x800) {
*(m_buf++) = static_cast<uint8_t>(0xC0 | c >> 6);
*(m_buf++) = static_cast<uint8_t>(0x80 | (c & 0x3F));
} else {
*(m_buf++) = static_cast<uint8_t>(0xE0 | c >> 12);
*(m_buf++) = static_cast<uint8_t>(0x80 | ((c >> 6) & 0x3F));
*(m_buf++) = static_cast<uint8_t>(0x80 | (c & 0x3F));
}
}
inline void writeNoCheck(uint8_t value) { *(m_buf++) = value; }
inline void writeNoCheck(int8_t value) {
writeNoCheck(static_cast<uint8_t>(value));
}
static uint8_t* checkoutBuffer(uint32_t* size);
static void checkinBuffer(uint8_t* buffer, uint32_t size);
// disable copy constructor and assignment
DataOutput(const DataOutput&);
DataOutput& operator=(const DataOutput&);
};
} // namespace client
} // namespace geode
} // namespace apache
#endif // GEODE_GFCPP_DATAOUTPUT_H_