This commit is contained in:
NH
2025-04-17 20:38:35 +08:00
commit 40504ab11f
117 changed files with 62822 additions and 0 deletions

298
common/BufferPool.cpp Normal file
View File

@@ -0,0 +1,298 @@
/*
* Copyright: JessMA Open Source (ldcsaa@gmail.com)
*
* Author : Bruce Liang
* Website : https://github.com/ldcsaa
* Project : https://github.com/ldcsaa/HP-Socket
* Blog : http://www.cnblogs.com/ldcsaa
* Wiki : http://www.oschina.net/p/hp-socket
* QQ Group : 44636872, 75375912
*
* Licensed 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 "BufferPool.h"
#include "FuncHelper.h"
const DWORD TItem::DEFAULT_ITEM_CAPACITY = DEFAULT_BUFFER_CACHE_CAPACITY;
const DWORD CBufferPool::DEFAULT_MAX_CACHE_SIZE = 0;
const DWORD CBufferPool::DEFAULT_ITEM_CAPACITY = CItemPool::DEFAULT_ITEM_CAPACITY;
const DWORD CBufferPool::DEFAULT_ITEM_POOL_SIZE = CItemPool::DEFAULT_POOL_SIZE;
const DWORD CBufferPool::DEFAULT_ITEM_POOL_HOLD = CItemPool::DEFAULT_POOL_HOLD;
const DWORD CBufferPool::DEFAULT_BUFFER_LOCK_TIME = DEFAULT_OBJECT_CACHE_LOCK_TIME;
const DWORD CBufferPool::DEFAULT_BUFFER_POOL_SIZE = DEFAULT_OBJECT_CACHE_POOL_SIZE;
const DWORD CBufferPool::DEFAULT_BUFFER_POOL_HOLD = DEFAULT_OBJECT_CACHE_POOL_HOLD;
int TItem::Cat(const BYTE* pData, int length)
{
ASSERT(pData != nullptr && length >= 0);
int cat = MIN(Remain(), length);
if(cat > 0)
{
memcpy(end, pData, cat);
end += cat;
}
return cat;
}
int TItem::Cat(const TItem& other)
{
ASSERT(this != &other);
return Cat(other.Ptr(), other.Size());
}
int TItem::Fetch(BYTE* pData, int length)
{
ASSERT(pData != nullptr && length > 0);
int fetch = MIN(Size(), length);
memcpy(pData, begin, fetch);
begin += fetch;
return fetch;
}
int TItem::Peek(BYTE* pData, int length)
{
ASSERT(pData != nullptr && length > 0);
int peek = MIN(Size(), length);
memcpy(pData, begin, peek);
return peek;
}
int TItem::Increase(int length)
{
ASSERT(length >= 0);
int increase = MIN(Remain(), length);
end += increase;
return increase;
}
int TItem::Reduce(int length)
{
ASSERT(length >= 0);
int reduce = MIN(Size(), length);
begin += reduce;
return reduce;
}
void TItem::Reset(int first, int last)
{
ASSERT(first >= -1 && first <= capacity);
ASSERT(last >= -1 && last <= capacity);
if(first >= 0) begin = head + MIN(first, capacity);
if(last >= 0) end = head + MIN(last, capacity);
}
TBuffer* TBuffer::Construct(CBufferPool& pool, ULONG_PTR dwID)
{
ASSERT(dwID != 0);
CPrivateHeap& heap = pool.GetPrivateHeap();
TBuffer* pBuffer = (TBuffer*)heap.Alloc(sizeof(TBuffer));
return ::ConstructObject(pBuffer, heap, pool.GetItemPool(), dwID);
}
void TBuffer::Destruct(TBuffer* pBuffer)
{
ASSERT(pBuffer != nullptr);
CPrivateHeap& heap = pBuffer->heap;
::DestructObject(pBuffer);
heap.Free(pBuffer);
}
void TBuffer::Reset()
{
id = 0;
length = 0;
freeTime = ::TimeGetTime();
}
int TBuffer::Cat(const BYTE* pData, int len)
{
items.Cat(pData, len);
return IncreaseLength(len);
}
int TBuffer::Cat(const TItem* pItem)
{
items.Cat(pItem);
return IncreaseLength(pItem->Size());
}
int TBuffer::Cat(const TItemList& other)
{
ASSERT(&items != &other);
for(TItem* pItem = other.Front(); pItem != nullptr; pItem = pItem->next)
Cat(pItem);
return length;
}
int TBuffer::Fetch(BYTE* pData, int len)
{
int fetch = items.Fetch(pData, len);
DecreaseLength(fetch);
return fetch;
}
int TBuffer::Peek(BYTE* pData, int len)
{
return items.Peek(pData, len);
}
int TBuffer::Reduce(int len)
{
int reduce = items.Reduce(len);
DecreaseLength(reduce);
return reduce;
}
void CBufferPool::PutFreeBuffer(ULONG_PTR dwID)
{
ASSERT(dwID != 0);
TBuffer* pBuffer = FindCacheBuffer(dwID);
if(pBuffer != nullptr)
PutFreeBuffer(pBuffer);
}
void CBufferPool::PutFreeBuffer(TBuffer* pBuffer)
{
ASSERT(pBuffer != nullptr);
if(!pBuffer->IsValid())
return;
m_bfCache.RemoveEx(pBuffer->ID());
BOOL bOK = FALSE;
{
CCriSecLock locallock(pBuffer->cs);
if(pBuffer->IsValid())
{
pBuffer->Reset();
bOK = TRUE;
}
}
if(bOK)
{
m_itPool.PutFreeItem(pBuffer->items);
#ifndef USE_EXTERNAL_GC
ReleaseGCBuffer();
#endif
if(!m_lsFreeBuffer.TryPut(pBuffer))
m_lsGCBuffer.PushBack(pBuffer);
}
}
void CBufferPool::ReleaseGCBuffer(BOOL bForce)
{
::ReleaseGCObj(m_lsGCBuffer, m_dwBufferLockTime, bForce);
}
TBuffer* CBufferPool::PutCacheBuffer(ULONG_PTR dwID)
{
ASSERT(dwID != 0);
TBuffer* pBuffer = PickFreeBuffer(dwID);
m_bfCache.SetEx(dwID, pBuffer);
return pBuffer;
}
TBuffer* CBufferPool::PickFreeBuffer(ULONG_PTR dwID)
{
ASSERT( dwID != 0);
DWORD dwIndex;
TBuffer* pBuffer = nullptr;
if(m_lsFreeBuffer.TryLock(&pBuffer, dwIndex))
{
if(::GetTimeGap32(pBuffer->freeTime) >= m_dwBufferLockTime)
VERIFY(m_lsFreeBuffer.ReleaseLock(nullptr, dwIndex));
else
{
VERIFY(m_lsFreeBuffer.ReleaseLock(pBuffer, dwIndex));
pBuffer = nullptr;
}
}
if(pBuffer) pBuffer->id = dwID;
else pBuffer = TBuffer::Construct(*this, dwID);
ASSERT(pBuffer);
return pBuffer;
}
TBuffer* CBufferPool::FindCacheBuffer(ULONG_PTR dwID)
{
ASSERT(dwID != 0);
TBuffer* pBuffer = nullptr;
if(m_bfCache.GetEx(dwID, &pBuffer) != TBufferCache::GR_VALID)
pBuffer = nullptr;
return pBuffer;
}
void CBufferPool::Prepare()
{
m_itPool.Prepare();
m_bfCache.Reset(m_dwMaxCacheSize);
m_lsFreeBuffer.Reset(m_dwBufferPoolSize);
}
void CBufferPool::Clear()
{
TBufferCache::IndexSet& indexes = m_bfCache.Indexes();
for(auto it = indexes.begin(), end = indexes.end(); it != end; ++it)
{
TBuffer* pBuffer = FindCacheBuffer(*it);
if(pBuffer) TBuffer::Destruct(pBuffer);
}
m_bfCache.Reset();
m_lsFreeBuffer.Clear();
ReleaseGCBuffer(TRUE);
VERIFY(m_lsGCBuffer.IsEmpty());
m_itPool.Clear();
m_heap.Reset();
}

869
common/BufferPool.h Normal file
View File

@@ -0,0 +1,869 @@
/*
* Copyright: JessMA Open Source (ldcsaa@gmail.com)
*
* Author : Bruce Liang
* Website : https://github.com/ldcsaa
* Project : https://github.com/ldcsaa/HP-Socket
* Blog : http://www.cnblogs.com/ldcsaa
* Wiki : http://www.oschina.net/p/hp-socket
* QQ Group : 44636872, 75375912
*
* Licensed 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.
*/
#pragma once
#include "hpsocket/GlobalDef.h"
#include "Singleton.h"
#include "STLHelper.h"
#include "RingBuffer.h"
#include "PrivateHeap.h"
#include "CriSec.h"
template<class T> T* ConstructItemT(T*, CPrivateHeap& heap, int capacity, BYTE* pData, int length)
{
ASSERT(capacity > 0);
int item_size = sizeof(T);
T* pItem = (T*)heap.Alloc(item_size + capacity);
BYTE* pHead = (BYTE*)pItem + item_size;
return ::ConstructObject(pItem, heap, pHead, capacity, pData, length);
}
template<class T> void DestructItemT(T* pItem)
{
ASSERT(pItem != nullptr);
CPrivateHeap& heap = pItem->GetPrivateHeap();
::DestructObject(pItem);
heap.Free(pItem);
}
struct TItem
{
template<typename T> friend struct TSimpleList;
template<typename T> friend class CNodePoolT;
template<typename T> friend struct TItemListT;
friend struct TBuffer;
public:
int Cat (const BYTE* pData, int length);
int Cat (const TItem& other);
int Fetch (BYTE* pData, int length);
int Peek (BYTE* pData, int length);
int Increase(int length);
int Reduce (int length);
void Reset (int first = 0, int last = 0);
BYTE* Ptr () {return begin;}
const BYTE* Ptr () const {return begin;}
int Size () const {return (int)(end - begin);}
int Remain () const {return capacity - (int)(end - head);}
int Capacity() const {return capacity;}
bool IsEmpty () const {return Size() == 0;}
bool IsFull () const {return Remain() == 0;}
CPrivateHeap& GetPrivateHeap() {return heap;}
operator BYTE* () {return Ptr();}
operator const BYTE* () const {return Ptr();}
public:
static TItem* Construct(CPrivateHeap& heap,
int capacity = DEFAULT_ITEM_CAPACITY,
BYTE* pData = nullptr,
int length = 0)
{
return ::ConstructItemT((TItem*)(nullptr), heap, capacity, pData, length);
}
static void Destruct(TItem* pItem)
{
::DestructItemT(pItem);
}
TItem(CPrivateHeap& hp, BYTE* pHead, int cap = DEFAULT_ITEM_CAPACITY, BYTE* pData = nullptr, int length = 0)
: heap(hp), head(pHead), begin(pHead), end(pHead), capacity(cap), next(nullptr), last(nullptr)
{
if(pData != nullptr && length != 0)
Cat(pData, length);
}
~TItem() {}
DECLARE_NO_COPY_CLASS(TItem)
public:
static const DWORD DEFAULT_ITEM_CAPACITY;
private:
CPrivateHeap& heap;
private:
TItem* next;
TItem* last;
int capacity;
BYTE* head;
BYTE* begin;
BYTE* end;
};
template<class T> struct TSimpleList
{
public:
T* PushFront(T* pItem)
{
if(pFront != nullptr)
{
pFront->last = pItem;
pItem->next = pFront;
}
else
{
pItem->last = nullptr;
pItem->next = nullptr;
pBack = pItem;
}
pFront = pItem;
++size;
return pItem;
}
T* PushBack(T* pItem)
{
if(pBack != nullptr)
{
pBack->next = pItem;
pItem->last = pBack;
}
else
{
pItem->last = nullptr;
pItem->next = nullptr;
pFront = pItem;
}
pBack = pItem;
++size;
return pItem;
}
T* PopFront()
{
T* pItem = pFront;
if(pFront != pBack)
{
pFront = (T*)pFront->next;
pFront->last = nullptr;
}
else if(pFront != nullptr)
{
pFront = nullptr;
pBack = nullptr;
}
if(pItem != nullptr)
{
pItem->next = nullptr;
pItem->last = nullptr;
--size;
}
return pItem;
}
T* PopBack()
{
T* pItem = pBack;
if(pFront != pBack)
{
pBack = (T*)pBack->last;
pBack->next = nullptr;
}
else if(pBack != nullptr)
{
pFront = nullptr;
pBack = nullptr;
}
if(pItem != nullptr)
{
pItem->next = nullptr;
pItem->last = nullptr;
--size;
}
return pItem;
}
TSimpleList<T>& Shift(TSimpleList<T>& other)
{
if(&other != this && other.size > 0)
{
if(size > 0)
{
pBack->next = other.pFront;
other.pFront->last = pBack;
}
else
{
pFront = other.pFront;
}
pBack = other.pBack;
size += other.size;
other.Reset();
}
return *this;
}
void Clear()
{
if(size > 0)
{
T* pItem;
while((pItem = PopFront()) != nullptr)
T::Destruct(pItem);
}
}
T* Front () const {return pFront;}
T* Back () const {return pBack;}
int Size () const {return size;}
bool IsEmpty () const {return size == 0;}
public:
TSimpleList() {Reset();}
~TSimpleList() {Clear();}
DECLARE_NO_COPY_CLASS(TSimpleList<T>)
private:
void Reset()
{
pFront = nullptr;
pBack = nullptr;
size = 0;
}
private:
int size;
T* pFront;
T* pBack;
};
template<class T> class CNodePoolT
{
public:
void PutFreeItem(T* pItem)
{
ASSERT(pItem != nullptr);
if(!m_lsFreeItem.TryPut(pItem))
T::Destruct(pItem);
}
void PutFreeItem(TSimpleList<T>& lsItem)
{
if(lsItem.IsEmpty())
return;
T* pItem;
while((pItem = lsItem.PopFront()) != nullptr)
PutFreeItem(pItem);
}
T* PickFreeItem()
{
T* pItem = nullptr;
if(!m_lsFreeItem.TryGet(&pItem))
pItem = T::Construct(m_heap, m_dwItemCapacity);
ASSERT(pItem);
pItem->Reset();
return pItem;
}
void Prepare()
{
m_lsFreeItem.Reset(m_dwPoolSize);
}
void Clear()
{
m_lsFreeItem.Clear();
m_heap.Reset();
}
public:
void SetItemCapacity(DWORD dwItemCapacity) {m_dwItemCapacity = dwItemCapacity;}
void SetPoolSize (DWORD dwPoolSize) {m_dwPoolSize = dwPoolSize;}
void SetPoolHold (DWORD dwPoolHold) {m_dwPoolHold = dwPoolHold;}
DWORD GetItemCapacity () {return m_dwItemCapacity;}
DWORD GetPoolSize () {return m_dwPoolSize;}
DWORD GetPoolHold () {return m_dwPoolHold;}
CPrivateHeap& GetPrivateHeap() {return m_heap;}
public:
CNodePoolT( DWORD dwPoolSize = DEFAULT_POOL_SIZE,
DWORD dwPoolHold = DEFAULT_POOL_HOLD,
DWORD dwItemCapacity = DEFAULT_ITEM_CAPACITY)
: m_dwPoolSize(dwPoolSize)
, m_dwPoolHold(dwPoolHold)
, m_dwItemCapacity(dwItemCapacity)
{
}
~CNodePoolT() {Clear();}
DECLARE_NO_COPY_CLASS(CNodePoolT)
public:
static const DWORD DEFAULT_ITEM_CAPACITY;
static const DWORD DEFAULT_POOL_SIZE;
static const DWORD DEFAULT_POOL_HOLD;
private:
CPrivateHeap m_heap;
DWORD m_dwItemCapacity;
DWORD m_dwPoolSize;
DWORD m_dwPoolHold;
CRingPool<T> m_lsFreeItem;
};
template<class T> const DWORD CNodePoolT<T>::DEFAULT_ITEM_CAPACITY = TItem::DEFAULT_ITEM_CAPACITY;
template<class T> const DWORD CNodePoolT<T>::DEFAULT_POOL_SIZE = DEFAULT_BUFFER_CACHE_POOL_SIZE;
template<class T> const DWORD CNodePoolT<T>::DEFAULT_POOL_HOLD = DEFAULT_BUFFER_CACHE_POOL_HOLD;
using CItemPool = CNodePoolT<TItem>;
template<class T> struct TItemListT : public TSimpleList<T>
{
using __super = TSimpleList<T>;
public:
int PushTail(const BYTE* pData, int length)
{
ASSERT(length <= (int)itPool.GetItemCapacity());
if(length > (int)itPool.GetItemCapacity())
return 0;
T* pItem = __super::PushBack(itPool.PickFreeItem());
return pItem->Cat(pData, length);
}
int Cat(const BYTE* pData, int length)
{
int remain = length;
while(remain > 0)
{
T* pItem = __super::Back();
if(pItem == nullptr || pItem->IsFull())
pItem = __super::PushBack(itPool.PickFreeItem());
int cat = pItem->Cat(pData, remain);
pData += cat;
remain -= cat;
}
return length;
}
int Cat(const T* pItem)
{
return Cat(pItem->Ptr(), pItem->Size());
}
int Cat(const TItemListT<T>& other)
{
ASSERT(this != &other);
int length = 0;
for(T* pItem = other.Front(); pItem != nullptr; pItem = pItem->next)
length += Cat(pItem);
return length;
}
int Fetch(BYTE* pData, int length)
{
int remain = length;
while(remain > 0 && __super::Size() > 0)
{
T* pItem = __super::Front();
int fetch = pItem->Fetch(pData, remain);
pData += fetch;
remain -= fetch;
if(pItem->IsEmpty())
itPool.PutFreeItem(__super::PopFront());
}
return length - remain;
}
int Peek(BYTE* pData, int length)
{
int remain = length;
T* pItem = __super::Front();
while(remain > 0 && pItem != nullptr)
{
int peek = pItem->Peek(pData, remain);
pData += peek;
remain -= peek;
pItem = pItem->next;
}
return length - remain;
}
int Increase(int length)
{
int remain = length;
while(remain > 0)
{
T* pItem = __super::Back();
if(pItem == nullptr || pItem->IsFull())
{
pItem = itPool.PickFreeItem();
__super::PushBack(pItem);
}
remain -= pItem->Increase(remain);
}
return length - remain;
}
int Reduce(int length)
{
int remain = length;
while(remain > 0 && __super::Size() > 0)
{
T* pItem = __super::Front();
remain -= pItem->Reduce(remain);
if(pItem->IsEmpty())
itPool.PutFreeItem(__super::PopFront());
}
return length - remain;
}
void Release()
{
itPool.PutFreeItem(*this);
}
CNodePoolT<T>& GetItemPool() {return itPool;}
public:
TItemListT(CNodePoolT<T>& pool) : itPool(pool)
{
}
private:
CNodePoolT<T>& itPool;
};
using TItemList = TItemListT<TItem>;
template<class T, class length_t = int, typename = enable_if_t<is_integral<typename decay<length_t>::type>::value>>
struct TItemListExT : public TItemListT<T>
{
using __super = TItemListT<T>;
public:
T* PushFront(T* pItem)
{
length += pItem->Size();
return __super::PushFront(pItem);
}
T* PushBack(T* pItem)
{
length += pItem->Size();
return __super::PushBack(pItem);
}
T* PopFront()
{
T* pItem = __super::PopFront();
if(pItem != nullptr)
length -= pItem->Size();
return pItem;
}
T* PopBack()
{
T* pItem = __super::PopBack();
if(pItem != nullptr)
length -= pItem->Size();
return pItem;
}
TItemListExT& Shift(TItemListExT<T>& other)
{
if(&other != this && other.length > 0)
{
length += other.length;
other.length = 0;
__super::Shift(other);
}
return *this;
}
void Clear()
{
__super::Clear();
length = 0;
}
void Release()
{
__super::Release();
length = 0;
}
public:
int PushTail(const BYTE* pData, int length)
{
int cat = __super::PushTail(pData, length);
this->length += cat;
return cat;
}
int Cat(const BYTE* pData, int length)
{
int cat = __super::Cat(pData, length);
this->length += cat;
return cat;
}
int Cat(const T* pItem)
{
int cat = __super::Cat(pItem->Ptr(), pItem->Size());
this->length += cat;
return cat;
}
int Cat(const TItemListT<T>& other)
{
int cat = __super::Cat(other);
this->length += cat;
return cat;
}
int Fetch(BYTE* pData, int length)
{
int fetch = __super::Fetch(pData, length);
this->length -= fetch;
return fetch;
}
int Increase(int length)
{
int increase = __super::Increase(length);
this->length += increase;
return increase;
}
int Reduce(int length)
{
int reduce = __super::Reduce(length);
this->length -= reduce;
return reduce;
}
typename decay<length_t>::type Length() const {return length;}
int IncreaseLength (int length) {return (this->length += length);}
int ReduceLength (int length) {return (this->length -= length);}
public:
TItemListExT(CNodePoolT<T>& pool) : TItemListT<T>(pool), length(0)
{
}
~TItemListExT()
{
ASSERT(length >= 0);
}
DECLARE_NO_COPY_CLASS(TItemListExT)
private:
length_t length;
};
using TItemListEx = TItemListExT<TItem>;
using TItemListExV = TItemListExT<TItem, volatile int>;
template<class T> struct TItemPtrT
{
public:
T* Reset(T* pItem = nullptr)
{
if(m_pItem != nullptr)
itPool.PutFreeItem(m_pItem);
m_pItem = pItem;
return m_pItem;
}
T* Attach(T* pItem)
{
return Reset(pItem);
}
T* Detach()
{
T* pItem = m_pItem;
m_pItem = nullptr;
return pItem;
}
T* New()
{
return Attach(itPool.PickFreeItem());
}
bool IsValid () {return m_pItem != nullptr;}
T* operator -> () {return m_pItem;}
T* operator = (T* pItem) {return Reset(pItem);}
operator T* () {return m_pItem;}
T*& PtrRef () {return m_pItem;}
T* Ptr () {return m_pItem;}
const T* Ptr () const {return m_pItem;}
operator const T* () const {return m_pItem;}
public:
TItemPtrT(CNodePoolT<T>& pool, T* pItem = nullptr)
: itPool(pool), m_pItem(pItem)
{
}
TItemPtrT(TItemListT<T>& ls, T* pItem = nullptr)
: itPool(ls.GetItemPool()), m_pItem(pItem)
{
}
~TItemPtrT()
{
Reset();
}
DECLARE_NO_COPY_CLASS(TItemPtrT)
private:
CNodePoolT<T>& itPool;
T* m_pItem;
};
using TItemPtr = TItemPtrT<TItem>;
class CBufferPool;
struct TBuffer
{
template<typename T> friend struct TSimpleList;
friend class CBufferPool;
public:
static TBuffer* Construct(CBufferPool& pool, ULONG_PTR dwID);
static void Destruct(TBuffer* pBuffer);
public:
int Cat (const BYTE* pData, int len);
int Cat (const TItem* pItem);
int Cat (const TItemList& other);
int Fetch (BYTE* pData, int length);
int Peek (BYTE* pData, int length);
int Reduce (int len);
public:
CCriSec& CriSec () {return cs;}
TItemList& ItemList() {return items;}
ULONG_PTR ID () const {return id;}
int Length () const {return length;}
bool IsValid () const {return id != 0;}
DWORD GetFreeTime () const {return freeTime;}
int GetCount () const {return 0;}
private:
int IncreaseLength (int len) {return (length += len);}
int DecreaseLength (int len) {return (length -= len);}
void Reset ();
private:
friend TBuffer* ConstructObject<>(TBuffer*, CPrivateHeap&, CItemPool&, ULONG_PTR&);
friend void DestructObject<>(TBuffer*);
TBuffer(CPrivateHeap& hp, CItemPool& itPool, ULONG_PTR dwID = 0)
: heap(hp), items(itPool), id(dwID), length(0)
{
}
~TBuffer() {}
DECLARE_NO_COPY_CLASS(TBuffer)
private:
CPrivateHeap& heap;
private:
ULONG_PTR id;
int length;
DWORD freeTime;
private:
TBuffer* next;
TBuffer* last;
CCriSec cs;
TItemList items;
};
class CBufferPool
{
using TBufferList = CRingPool<TBuffer>;
using TBufferQueue = CCASQueue<TBuffer>;
using TBufferCache = CRingCache<TBuffer, ULONG_PTR, true>;
public:
void PutFreeBuffer (ULONG_PTR dwID);
TBuffer* PutCacheBuffer (ULONG_PTR dwID);
TBuffer* FindCacheBuffer (ULONG_PTR dwID);
TBuffer* PickFreeBuffer (ULONG_PTR dwID);
void PutFreeBuffer (TBuffer* pBuffer);
void Prepare ();
void Clear ();
void ReleaseGCBuffer (BOOL bForce = FALSE);
public:
void SetItemCapacity (DWORD dwItemCapacity) {m_itPool.SetItemCapacity(dwItemCapacity);}
void SetItemPoolSize (DWORD dwItemPoolSize) {m_itPool.SetPoolSize(dwItemPoolSize);}
void SetItemPoolHold (DWORD dwItemPoolHold) {m_itPool.SetPoolHold(dwItemPoolHold);}
void SetMaxCacheSize (DWORD dwMaxCacheSize) {m_dwMaxCacheSize = dwMaxCacheSize;}
void SetBufferLockTime (DWORD dwBufferLockTime) {m_dwBufferLockTime = dwBufferLockTime;}
void SetBufferPoolSize (DWORD dwBufferPoolSize) {m_dwBufferPoolSize = dwBufferPoolSize;}
void SetBufferPoolHold (DWORD dwBufferPoolHold) {m_dwBufferPoolHold = dwBufferPoolHold;}
DWORD GetItemCapacity () {return m_itPool.GetItemCapacity();}
DWORD GetItemPoolSize () {return m_itPool.GetPoolSize();}
DWORD GetItemPoolHold () {return m_itPool.GetPoolHold();}
DWORD GetMaxCacheSize () {return m_dwMaxCacheSize;}
DWORD GetBufferLockTime () {return m_dwBufferLockTime;}
DWORD GetBufferPoolSize () {return m_dwBufferPoolSize;}
DWORD GetBufferPoolHold () {return m_dwBufferPoolHold;}
TBuffer* operator [] (ULONG_PTR dwID) {return FindCacheBuffer(dwID);}
public:
CBufferPool(DWORD dwPoolSize = DEFAULT_BUFFER_POOL_SIZE,
DWORD dwPoolHold = DEFAULT_BUFFER_POOL_HOLD,
DWORD dwLockTime = DEFAULT_BUFFER_LOCK_TIME,
DWORD dwMaxCacheSize = DEFAULT_MAX_CACHE_SIZE)
: m_dwBufferPoolSize(dwPoolSize)
, m_dwBufferPoolHold(dwPoolHold)
, m_dwBufferLockTime(dwLockTime)
, m_dwMaxCacheSize(dwMaxCacheSize)
{
}
~CBufferPool() {Clear();}
DECLARE_NO_COPY_CLASS(CBufferPool)
public:
CPrivateHeap& GetPrivateHeap() {return m_heap;}
CItemPool& GetItemPool() {return m_itPool;}
public:
static const DWORD DEFAULT_MAX_CACHE_SIZE;
static const DWORD DEFAULT_ITEM_CAPACITY;
static const DWORD DEFAULT_ITEM_POOL_SIZE;
static const DWORD DEFAULT_ITEM_POOL_HOLD;
static const DWORD DEFAULT_BUFFER_LOCK_TIME;
static const DWORD DEFAULT_BUFFER_POOL_SIZE;
static const DWORD DEFAULT_BUFFER_POOL_HOLD;
private:
DWORD m_dwMaxCacheSize;
DWORD m_dwBufferLockTime;
DWORD m_dwBufferPoolSize;
DWORD m_dwBufferPoolHold;
CPrivateHeap m_heap;
CItemPool m_itPool;
TBufferCache m_bfCache;
TBufferList m_lsFreeBuffer;
TBufferQueue m_lsGCBuffer;
};

198
common/BufferPtr.h Normal file
View File

@@ -0,0 +1,198 @@
/*
* Copyright: JessMA Open Source (ldcsaa@gmail.com)
*
* Author : Bruce Liang
* Website : https://github.com/ldcsaa
* Project : https://github.com/ldcsaa/HP-Socket
* Blog : http://www.cnblogs.com/ldcsaa
* Wiki : http://www.oschina.net/p/hp-socket
* QQ Group : 44636872, 75375912
*
* Licensed 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.
*/
#pragma once
#include "hpsocket/GlobalDef.h"
#include <memory.h>
#include <malloc.h>
template<class T, size_t MAX_CACHE_SIZE = 0>
class CBufferPtrT
{
public:
explicit CBufferPtrT(size_t size = 0, bool zero = false) {Reset(); Malloc(size, zero);}
explicit CBufferPtrT(const T* pch, size_t size) {Reset(); Copy(pch, size);}
CBufferPtrT(const CBufferPtrT& other) {Reset(); Copy(other);}
template<size_t S> CBufferPtrT(const CBufferPtrT<T, S>& other) {Reset(); Copy(other);}
~CBufferPtrT() {Free();}
T* Malloc(size_t size = 1, bool zero = false)
{
Free();
return Alloc(size, zero, false);
}
T* Realloc(size_t size, bool zero = false)
{
return Alloc(size, zero, true);
}
void Free()
{
if(m_pch)
{
free(m_pch);
Reset();
}
}
template<size_t S> CBufferPtrT& Copy(const CBufferPtrT<T, S>& other)
{
if((void*)&other != (void*)this)
Copy(other.Ptr(), other.Size());
return *this;
}
CBufferPtrT& Copy(const T* pch, size_t size)
{
Malloc(size);
if(m_pch)
memcpy(m_pch, pch, size * sizeof(T));
return *this;
}
template<size_t S> CBufferPtrT& Cat(const CBufferPtrT<T, S>& other)
{
if((void*)&other != (void*)this)
Cat(other.Ptr(), other.Size());
return *this;
}
CBufferPtrT& Cat(const T* pch, size_t size = 1)
{
size_t pre_size = m_size;
Realloc(m_size + size);
if(m_pch)
memcpy(m_pch + pre_size, pch, size * sizeof(T));
return *this;
}
template<size_t S> bool Equal(const CBufferPtrT<T, S>& other) const
{
if((void*)&other == (void*)this)
return true;
else if(m_size != other.Size())
return false;
else if(m_size == 0)
return true;
else
return (memcmp(m_pch, other.Ptr(), m_size * sizeof(T)) == 0);
}
bool Equal(T* pch) const
{
if(m_pch == pch)
return true;
else if(!m_pch || !pch)
return false;
else
return (memcmp(m_pch, pch, m_size * sizeof(T)) == 0);
}
size_t SetSize(size_t size)
{
if(size < 0 || size > m_capacity)
size = m_capacity;
return (m_size = size);
}
T* Ptr() {return m_pch;}
const T* Ptr() const {return m_pch;}
T& Get(int i) {return *(m_pch + i);}
const T& Get(int i) const {return *(m_pch + i);}
size_t Size() const {return m_size;}
size_t Capacity() const {return m_capacity;}
bool IsValid() const {return m_pch != 0;}
operator T* () {return Ptr();}
operator const T* () const {return Ptr();}
T& operator [] (int i) {return Get(i);}
const T& operator [] (int i) const {return Get(i);}
bool operator == (T* pv) const {return Equal(pv);}
template<size_t S> bool operator == (const CBufferPtrT<T, S>& other) {return Equal(other);}
CBufferPtrT& operator = (const CBufferPtrT& other) {return Copy(other);}
template<size_t S> CBufferPtrT& operator = (const CBufferPtrT<T, S>& other) {return Copy(other);}
private:
void Reset() {m_pch = 0; m_size = 0; m_capacity = 0;}
size_t GetAllocSize(size_t size) {return MAX(size, MIN(size * 2, m_size + MAX_CACHE_SIZE));}
T* Alloc(size_t size, bool zero = false, bool is_realloc = false)
{
if(size != m_size)
{
size_t rsize = GetAllocSize(size);
if(size > m_capacity || rsize < m_size)
{
T* pch = is_realloc ?
(T*)realloc(m_pch, rsize * sizeof(T)) :
(T*)malloc(rsize * sizeof(T)) ;
if(pch || rsize == 0)
{
m_pch = pch;
m_size = size;
m_capacity = rsize;
}
else
{
Free();
throw std::bad_alloc();
}
}
else
m_size = size;
}
if(zero && m_pch)
memset(m_pch, 0, m_size * sizeof(T));
return m_pch;
}
private:
T* m_pch;
size_t m_size;
size_t m_capacity;
};
typedef CBufferPtrT<char> CCharBufferPtr;
typedef CBufferPtrT<wchar_t> CWCharBufferPtr;
typedef CBufferPtrT<unsigned char> CByteBufferPtr;
typedef CByteBufferPtr CBufferPtr;
#ifdef _UNICODE
typedef CWCharBufferPtr CTCharBufferPtr;
#else
typedef CCharBufferPtr CTCharBufferPtr;
#endif

291
common/CriSec.h Normal file
View File

@@ -0,0 +1,291 @@
/*
* Copyright: JessMA Open Source (ldcsaa@gmail.com)
*
* Author : Bruce Liang
* Website : https://github.com/ldcsaa
* Project : https://github.com/ldcsaa/HP-Socket
* Blog : http://www.cnblogs.com/ldcsaa
* Wiki : http://www.oschina.net/p/hp-socket
* QQ Group : 44636872, 75375912
*
* Licensed 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.
*/
#pragma once
#include "hpsocket/GlobalDef.h"
#include "Singleton.h"
#include "FuncHelper.h"
#include <mutex>
#include <atomic>
using namespace std;
class CSpinGuard
{
public:
CSpinGuard() : m_atFlag(FALSE)
{
}
~CSpinGuard()
{
ASSERT(!m_atFlag);
}
void Lock(BOOL bWeek = TRUE, memory_order m = memory_order_acquire)
{
for(UINT i = 0; !TryLock(bWeek, m); ++i)
YieldThread(i);
}
BOOL TryLock(BOOL bWeek = FALSE, memory_order m = memory_order_acquire)
{
BOOL bExpect = FALSE;
return bWeek
? m_atFlag.compare_exchange_weak(bExpect, TRUE, m)
: m_atFlag.compare_exchange_strong(bExpect, TRUE, m);
}
void Unlock(memory_order m = memory_order_release)
{
ASSERT(m_atFlag);
m_atFlag.store(FALSE, m);
}
DECLARE_NO_COPY_CLASS(CSpinGuard)
private:
atomic<BOOL> m_atFlag;
};
class CReentrantSpinGuard
{
public:
CReentrantSpinGuard()
: m_atThreadID (0)
, m_iCount (0)
{
}
~CReentrantSpinGuard()
{
ASSERT(m_atThreadID == 0);
ASSERT(m_iCount == 0);
}
void Lock(BOOL bWeek = TRUE, memory_order m = memory_order_acquire)
{
for(UINT i = 0; !_TryLock(i == 0, bWeek, m); ++i)
YieldThread(i);
}
BOOL TryLock(BOOL bWeek = FALSE, memory_order m = memory_order_acquire)
{
return _TryLock(TRUE, bWeek, m);
}
void Unlock(memory_order m = memory_order_release)
{
ASSERT(::IsSelfThread(m_atThreadID));
if((--m_iCount) == 0)
m_atThreadID.store(0, m);
}
private:
BOOL _TryLock(BOOL bFirst, BOOL bWeek = FALSE, memory_order m = memory_order_acquire)
{
THR_ID dwCurrentThreadID = SELF_THREAD_ID;
if(bFirst && ::IsSameThread(m_atThreadID, dwCurrentThreadID))
{
++m_iCount;
return TRUE;
}
THR_ID ulExpect = 0;
BOOL isOK = bWeek
? m_atThreadID.compare_exchange_weak(ulExpect, dwCurrentThreadID, m)
: m_atThreadID.compare_exchange_strong(ulExpect, dwCurrentThreadID, m);
if(isOK)
{
ASSERT(m_iCount == 0);
m_iCount = 1;
return TRUE;
}
return FALSE;
}
DECLARE_NO_COPY_CLASS(CReentrantSpinGuard)
private:
atomic_tid m_atThreadID;
int m_iCount;
};
class CFakeGuard
{
public:
void Lock() {}
void Unlock() {}
BOOL TryLock() {return TRUE;}
};
template<class CLockObj> class CLocalLock
{
public:
CLocalLock(CLockObj& obj) : m_lock(obj) {m_lock.Lock();}
~CLocalLock() {m_lock.Unlock();}
private:
CLockObj& m_lock;
};
template<class CLockObj> class CLocalTryLock
{
public:
CLocalTryLock(CLockObj& obj) : m_lock(obj) {m_bValid = m_lock.TryLock();}
~CLocalTryLock() {if(m_bValid) m_lock.Unlock();}
BOOL IsValid() {return m_bValid;}
private:
CLockObj& m_lock;
BOOL m_bValid;
};
template<class CMTXObj> class CMTXTryLock
{
public:
CMTXTryLock(CMTXObj& obj) : m_lock(obj) {m_bValid = m_lock.try_lock();}
~CMTXTryLock() {if(m_bValid) m_lock.unlock();}
BOOL IsValid() {return m_bValid;}
private:
CMTXObj& m_lock;
BOOL m_bValid;
};
using CSpinLock = CLocalLock<CSpinGuard>;
using CReentrantSpinLock = CLocalLock<CReentrantSpinGuard>;
using CFakeLock = CLocalLock<CFakeGuard>;
using CCriSec = mutex;
using CCriSecLock = lock_guard<mutex>;
using CCriSecLock2 = unique_lock<mutex>;
using CCriSecTryLock = CMTXTryLock<mutex>;
using CMTX = CCriSec;
using CMutexLock = CCriSecLock;
using CMutexLock2 = CCriSecLock2;
using CMutexTryLock = CCriSecTryLock;
using CReentrantCriSec = recursive_mutex;
using CReentrantCriSecLock = lock_guard<recursive_mutex>;
using CReentrantCriSecLock2 = unique_lock<recursive_mutex>;
using CReentrantCriSecTryLock = CMTXTryLock<recursive_mutex>;
using CReentrantMTX = CReentrantCriSec;
using CReentrantMutexLock = CReentrantCriSecLock;
using CReentrantMutexLock2 = CReentrantCriSecLock2;
using CReentrantMutexTryLock = CReentrantCriSecTryLock;
template<typename T, typename = enable_if_t<is_arithmetic<T>::value>> class CSafeCounterT
{
public:
T Increment() {return ::InterlockedIncrement(&m_iCount);}
T Decrement() {return ::InterlockedDecrement(&m_iCount);}
T AddFetch(T iCount) {return ::InterlockedAdd(&m_iCount, iCount);}
T SubFetch(T iCount) {return ::InterlockedSub(&m_iCount, iCount);}
T FetchAdd(T iCount) {return ::InterlockedExchangeAdd(&m_iCount, iCount);}
T FetchSub(T iCount) {return ::InterlockedExchangeSub(&m_iCount, iCount);}
T SetCount(T iCount) {return (m_iCount = iCount);}
T ResetCount() {return SetCount(0);}
T GetCount() {return m_iCount;}
T operator ++ () {return Increment();}
T operator -- () {return Decrement();}
T operator ++ (int) {return FetchAdd(1);}
T operator -- (int) {return FetchSub(1);}
T operator += (T iCount) {return AddFetch(iCount);}
T operator -= (T iCount) {return SubFetch(iCount);}
T operator = (T iCount) {return SetCount(iCount);}
operator T () {return GetCount();}
public:
CSafeCounterT(T iCount = 0) : m_iCount(iCount) {}
protected:
volatile T m_iCount;
};
template<typename T, typename = enable_if_t<is_arithmetic<T>::value>> class CUnsafeCounterT
{
public:
T Increment() {return ++m_iCount;}
T Decrement() {return --m_iCount;}
T AddFetch(T iCount) {return m_iCount += iCount;}
T SubFetch(T iCount) {return m_iCount -= iCount;}
T FetchAdd(T iCount) {T rs = m_iCount; m_iCount += iCount; return rs;}
T FetchSub(T iCount) {T rs = m_iCount; m_iCount -= iCount; return rs;}
T SetCount(T iCount) {return (m_iCount = iCount);}
T ResetCount() {return SetCount(0);}
T GetCount() {return m_iCount;}
T operator ++ () {return Increment();}
T operator -- () {return Decrement();}
T operator ++ (int) {return FetchAdd(1);}
T operator -- (int) {return FetchSub(1);}
T operator += (T iCount) {return AddFetch(iCount);}
T operator -= (T iCount) {return SubFetch(iCount);}
T operator = (T iCount) {return SetCount(iCount);}
operator T () {return GetCount();}
public:
CUnsafeCounterT(T iCount = 0) : m_iCount(iCount) {}
protected:
T m_iCount;
};
template<class CCounter> class CLocalCounter
{
public:
CLocalCounter(CCounter& obj) : m_counter(obj) {m_counter.Increment();}
~CLocalCounter() {m_counter.Decrement();}
private:
CCounter& m_counter;
};
using CSafeCounter = CSafeCounterT<INT>;
using CSafeBigCounter = CSafeCounterT<LONGLONG>;
using CUnsafeCounter = CUnsafeCounterT<INT>;
using CUnsafeBigCounter = CUnsafeCounterT<LONGLONG>;
using CLocalSafeCounter = CLocalCounter<CSafeCounter>;
using CLocalSafeBigCounter = CLocalCounter<CSafeBigCounter>;
using CLocalUnsafeCounter = CLocalCounter<CUnsafeCounter>;
using CLocalUnsafeBigCounter = CLocalCounter<CUnsafeBigCounter>;

24
common/Event.cpp Normal file
View File

@@ -0,0 +1,24 @@
/*
* Copyright: JessMA Open Source (ldcsaa@gmail.com)
*
* Author : Bruce Liang
* Website : https://github.com/ldcsaa
* Project : https://github.com/ldcsaa/HP-Socket
* Blog : http://www.cnblogs.com/ldcsaa
* Wiki : http://www.oschina.net/p/hp-socket
* QQ Group : 44636872, 75375912
*
* Licensed 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 "Event.h"

530
common/Event.h Normal file
View File

@@ -0,0 +1,530 @@
/*
* Copyright: JessMA Open Source (ldcsaa@gmail.com)
*
* Author : Bruce Liang
* Website : https://github.com/ldcsaa
* Project : https://github.com/ldcsaa/HP-Socket
* Blog : http://www.cnblogs.com/ldcsaa
* Wiki : http://www.oschina.net/p/hp-socket
* QQ Group : 44636872, 75375912
*
* Licensed 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.
*/
#pragma once
#include "hpsocket/GlobalDef.h"
#include "Singleton.h"
#include "FuncHelper.h"
#include "PollHelper.h"
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/eventfd.h>
#include <sys/timerfd.h>
#include <sys/signalfd.h>
class CPipeEvent
{
public:
enum
{
EVT_1 = 0x01,
EVT_WAKEUP = EVT_1,
EVT_EXIT = 0x7F,
EVT_SIG_0 = 0x80,
EVT_SIG_MAX = EVT_SIG_0 + _NSIG,
};
public:
int Wait(long lTimeout = INFINITE, const sigset_t* pSigSet = nullptr)
{
pollfd pfd = {m_fd[0], POLLIN};
while(TRUE)
{
int rs = (int)::PollForSingleObject(pfd, lTimeout, pSigSet);
if(rs <= TIMEOUT) return rs;
if(pfd.revents & POLLIN)
{
BYTE v;
if(!Get(v))
return HAS_ERROR;
if(v == 0)
continue;
return (int)v;
}
if(pfd.revents & _POLL_ALL_ERROR_EVENTS)
{
::SetLastError(ERROR_BROKEN_PIPE);
return HAS_ERROR;
}
ASSERT(FALSE);
}
}
BOOL Set(BYTE bVal = EVT_WAKEUP)
{
ASSERT_CHECK_EINVAL(bVal != 0);
return VERIFY(write(m_fd[1], &bVal, 1) > 0);
}
BOOL Get(BYTE& v)
{
ASSERT(IsValid());
int rs = (int)read(m_fd[0], &v, 1);
if(IS_HAS_ERROR(rs))
{
if(IS_WOULDBLOCK_ERROR())
v = 0;
else
return FALSE;
}
else if(rs == 0)
{
::SetLastError(ERROR_BROKEN_PIPE);
return FALSE;
}
return TRUE;
}
BOOL Reset()
{
BYTE v;
while(TRUE)
{
if(!Get(v))
return FALSE;
if(v == 0)
break;
}
return TRUE;
}
BOOL SetSignal(BYTE bSigVal)
{
ASSERT_CHECK_EINVAL(bSigVal > 0 && bSigVal < _NSIG);
return Set((BYTE)(EVT_SIG_0 + bSigVal));
}
static inline BYTE ToSignalValue(int iWaitResult)
{
if(iWaitResult <= EVT_SIG_0 || iWaitResult >= EVT_SIG_MAX)
return 0;
return (BYTE)(iWaitResult - EVT_SIG_0);
}
BOOL IsValid() {return IS_VALID_FD(m_fd[0]) && IS_VALID_FD(m_fd[1]);}
operator FD () {return m_fd[0];}
FD GetFD () {return m_fd[0];}
public:
CPipeEvent()
{
VERIFY_IS_NO_ERROR(pipe2(m_fd, O_NONBLOCK | O_CLOEXEC));
VERIFY(::fcntl_SETFL(m_fd[0], O_NOATIME));
VERIFY(::fcntl_SETFL(m_fd[1], O_NOATIME));
}
~CPipeEvent()
{
close(m_fd[1]);
close(m_fd[0]);
}
DECLARE_NO_COPY_CLASS(CPipeEvent)
private:
FD m_fd[2] = {INVALID_FD, INVALID_FD};
};
template<bool is_sem_mode = false> class CCounterEvent
{
public:
eventfd_t Wait(long lTimeout = INFINITE, const sigset_t* pSigSet = nullptr)
{
pollfd pfd = {m_evt, POLLIN};
while(TRUE)
{
long rs = ::PollForSingleObject(pfd, lTimeout, pSigSet);
if(rs <= TIMEOUT) return (eventfd_t)rs;
if(pfd.revents & POLLIN)
{
eventfd_t v;
if(!Get(v))
return HAS_ERROR;
if(v == 0)
continue;
return v;
}
if(pfd.revents & _POLL_ALL_ERROR_EVENTS)
{
::SetLastError(ERROR_HANDLES_CLOSED);
return HAS_ERROR;
}
ASSERT(FALSE);
}
}
BOOL Set(eventfd_t val = 1)
{
ASSERT_CHECK_EINVAL(val > 0);
int rs = eventfd_write(m_evt, val);
return VERIFY_IS_NO_ERROR(rs);
}
BOOL Get(eventfd_t& v)
{
ASSERT(IsValid());
if(IS_HAS_ERROR(eventfd_read(m_evt, &v)))
{
if(IS_WOULDBLOCK_ERROR())
v = 0;
else
return FALSE;
}
return TRUE;
}
BOOL Reset()
{
eventfd_t v;
while(TRUE)
{
if(!Get(v))
return FALSE;
if(v == 0)
break;
}
return TRUE;
}
BOOL IsValid() {return IS_VALID_FD(m_evt);}
operator FD () {return m_evt;}
FD GetFD () {return m_evt;}
public:
CCounterEvent(int iInitCount = 0)
{
int iFlag = EFD_NONBLOCK | EFD_CLOEXEC | (is_sem_mode ? EFD_SEMAPHORE : 0);
m_evt = eventfd(iInitCount, iFlag);
VERIFY(IsValid());
}
~CCounterEvent()
{
if(IsValid()) close(m_evt);
}
DECLARE_NO_COPY_CLASS(CCounterEvent)
private:
FD m_evt = INVALID_FD;
};
using CSimpleEvent = CCounterEvent<false>;
using CSemaphoreEvent = CCounterEvent<true>;
using CEvt = CSimpleEvent;
class CTimerEvent
{
public:
ULLONG Wait(long lTimeout = INFINITE, const sigset_t* pSigSet = nullptr)
{
pollfd pfd = {m_tmr, POLLIN};
while(TRUE)
{
SSIZE_T rs = ::PollForSingleObject(pfd, lTimeout, pSigSet);
if(rs <= TIMEOUT) return (ULLONG)rs;
if(pfd.revents & POLLIN)
{
BOOL ok;
ULLONG v;
if(!Get(v, ok))
return HAS_ERROR;
if(!ok)
continue;
return v;
}
if(pfd.revents & _POLL_ALL_ERROR_EVENTS)
{
::SetLastError(ERROR_HANDLES_CLOSED);
return HAS_ERROR;
}
ASSERT(FALSE);
}
}
BOOL Set(LLONG llInterval, LLONG llStart = -1)
{
ASSERT_CHECK_EINVAL(llInterval >= 0L);
if(llStart < 0)
llStart = llInterval;
itimerspec its;
::MillisecondToTimespec(llStart, its.it_value);
::MillisecondToTimespec(llInterval, its.it_interval);
int rs = timerfd_settime(m_tmr, 0, &its, nullptr);
return VERIFY_IS_NO_ERROR(rs);
}
BOOL Get(ULLONG &v, BOOL& ok)
{
ASSERT(IsValid());
return ::ReadTimer(m_tmr, &v, &ok);
}
BOOL Reset()
{
BOOL ok;
ULLONG v;
while(TRUE)
{
if(!Get(v, ok))
return FALSE;
if(!ok)
break;
}
return TRUE;
}
BOOL GetTime(LLONG& lStart, LLONG& lInterval)
{
itimerspec its;
if(IS_HAS_ERROR(timerfd_gettime(m_tmr, &its)))
return FALSE;
lStart = ::TimespecToMillisecond(its.it_value);
lInterval = ::TimespecToMillisecond(its.it_interval);
return TRUE;
}
BOOL IsValid() {return IS_VALID_FD(m_tmr);}
operator FD () {return m_tmr;}
FD GetFD () {return m_tmr;}
public:
CTimerEvent(bool bRealTimeClock = FALSE)
{
int iCID = (bRealTimeClock ? CLOCK_REALTIME : CLOCK_MONOTONIC);
m_tmr = timerfd_create(iCID, TFD_NONBLOCK | TFD_CLOEXEC);
VERIFY(IsValid());
}
~CTimerEvent()
{
if(IsValid()) close(m_tmr);
}
DECLARE_NO_COPY_CLASS(CTimerEvent)
private:
FD m_tmr = INVALID_FD;
};
class CSignalEvent
{
public:
int Wait(signalfd_siginfo& sgInfo, long lTimeout = INFINITE, const sigset_t* pSigSet = nullptr)
{
m_dwTID = SELF_THREAD_ID;
pollfd pfd = {m_sig, POLLIN};
while(TRUE)
{
long rs = ::PollForSingleObject(pfd, lTimeout, pSigSet);
if(rs <= TIMEOUT) return (int)rs;
if(pfd.revents & POLLIN)
{
BOOL ok;
if(!Get(sgInfo, ok))
return HAS_ERROR;
if(!ok)
continue;
return sgInfo.ssi_signo;
}
if(pfd.revents & _POLL_ALL_ERROR_EVENTS)
{
::SetLastError(ERROR_HANDLES_CLOSED);
return HAS_ERROR;
}
ASSERT(FALSE);
}
m_dwTID = 0;
}
BOOL Set(int iSig, const sigval sgVal, THR_ID dwTID = 0)
{
if(dwTID == 0)
{
dwTID = m_dwTID;
if(dwTID == 0)
{
::SetLastError(ERROR_INVALID_STATE);
return FALSE;
}
}
#if !defined(__ANDROID__)
int rs = pthread_sigqueue(dwTID, iSig, sgVal);
#else
int rs = pthread_kill(dwTID, iSig);
#endif
return IS_NO_ERROR(rs);
}
BOOL Get(signalfd_siginfo& v, BOOL& ok)
{
ASSERT(IsValid());
static const SSIZE_T SIZE = sizeof(signalfd_siginfo);
if(read(m_sig, &v, SIZE) == SIZE)
ok = TRUE;
{
if(IS_WOULDBLOCK_ERROR())
ok = FALSE;
else
return FALSE;
}
return ok;
}
BOOL Reset()
{
BOOL ok;
signalfd_siginfo v;
while(TRUE)
{
if(!Get(v, ok))
return FALSE;
if(!ok)
break;
}
return TRUE;
}
BOOL Mask(const sigset_t* pSigMask)
{
if(!pSigMask)
{
if(!IsValid()) return TRUE;
return IS_NO_ERROR(close(m_sig));
}
FD sig = signalfd(m_sig, pSigMask, SFD_NONBLOCK | SFD_CLOEXEC);
if(IS_VALID_FD(sig))
{
m_sig = sig;
return TRUE;
}
return FALSE;
}
BOOL IsValid() {return IS_VALID_FD(m_sig);}
operator FD () {return m_sig;}
FD GetFD () {return m_sig;}
public:
CSignalEvent(const sigset_t* pSigMask = nullptr)
{
if(pSigMask) VERIFY(Mask(pSigMask));
}
~CSignalEvent()
{
if(IsValid()) close(m_sig);
}
DECLARE_NO_COPY_CLASS(CSignalEvent)
private:
FD m_sig = INVALID_FD;
THR_ID m_dwTID = 0;
};

237
common/FileHelper.cpp Normal file
View File

@@ -0,0 +1,237 @@
/*
* Copyright: JessMA Open Source (ldcsaa@gmail.com)
*
* Author : Bruce Liang
* Website : https://github.com/ldcsaa
* Project : https://github.com/ldcsaa/HP-Socket
* Blog : http://www.cnblogs.com/ldcsaa
* Wiki : http://www.oschina.net/p/hp-socket
* QQ Group : 44636872, 75375912
*
* Licensed 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 "FileHelper.h"
#include <sys/stat.h>
CString GetCurrentDirectory()
{
char szPath[MAX_PATH];
if(getcwd(szPath, sizeof(szPath) - 1) == nullptr)
szPath[0] = 0;
return szPath;
}
CString GetModuleFileName(pid_t pid)
{
if(pid == 0)
pid = SELF_PROCESS_ID;
char szLink[MAX_PATH];
char szPath[MAX_PATH];
sprintf(szLink, "/proc/%d/exe", pid);
SSIZE_T rs = readlink(szLink, szPath, sizeof(szPath) - 1);
if(rs < 0) rs = 0;
szPath[rs] = 0;
return szPath;
}
BOOL SetCurrentPathToModulePath(pid_t pid)
{
CString strPath = GetModuleFileName(pid);
if(strPath.IsEmpty())
return FALSE;
CString::size_type pos = strPath.rfind('/');
if(pos == CString::npos)
return FALSE;
return IS_NO_ERROR(chdir(strPath.substr(0, pos + 1)));
}
BOOL CFile::Open(LPCTSTR lpszFilePath, int iFlag, mode_t iMode)
{
CHECK_ERROR(!IsValid(), ERROR_INVALID_STATE);
m_fd = open(lpszFilePath, iFlag, iMode);
return IS_VALID_FD(m_fd);
}
BOOL CFile::Close()
{
CHECK_ERROR(IsValid(), ERROR_INVALID_STATE);
if(IS_NO_ERROR(close(m_fd)))
{
m_fd = INVALID_FD;
return TRUE;
}
return FALSE;
}
BOOL CFile::Stat(struct stat& st)
{
CHECK_ERROR_INVOKE(fstat(m_fd, &st));
return TRUE;
}
BOOL CFile::GetSize(SIZE_T& dwSize)
{
struct stat st;
CHECK_IS_OK(Stat(st));
dwSize = st.st_size;
return TRUE;
}
BOOL CFile::IsDirectory()
{
struct stat st;
CHECK_IS_OK(Stat(st));
return S_ISDIR(st.st_mode);
}
BOOL CFile::IsFile()
{
struct stat st;
CHECK_IS_OK(Stat(st));
return S_ISREG(st.st_mode);
}
BOOL CFile::IsExist(LPCTSTR lpszFilePath)
{
return IS_NO_ERROR(access(lpszFilePath, F_OK));
}
BOOL CFile::IsDirectory(LPCTSTR lpszFilePath)
{
struct stat st;
CHECK_ERROR_INVOKE(stat(lpszFilePath, &st));
return S_ISDIR(st.st_mode);
}
BOOL CFile::IsFile(LPCTSTR lpszFilePath)
{
struct stat st;
CHECK_ERROR_INVOKE(stat(lpszFilePath, &st));
return S_ISREG(st.st_mode);
}
BOOL CFile::IsLink(LPCTSTR lpszFilePath)
{
struct stat st;
CHECK_ERROR_INVOKE(lstat(lpszFilePath, &st));
return S_ISLNK(st.st_mode);
}
BOOL CFileMapping::Map(LPCTSTR lpszFilePath, SIZE_T dwSize, SIZE_T dwOffset, int iProtected, int iFlag)
{
CHECK_ERROR(!IsValid(), ERROR_INVALID_STATE);
FD fd = INVALID_FD;
if(lpszFilePath != nullptr)
{
int iFileFlag = O_RDONLY;
if(iProtected & PROT_WRITE)
{
if(iProtected & PROT_READ)
iFileFlag = O_RDWR;
else
iFileFlag = O_WRONLY;
}
fd = open(lpszFilePath, iFileFlag);
CHECK_ERROR_FD(fd);
}
BOOL isOK = Map(fd, dwSize, dwOffset, iProtected, iFlag);
if(IS_VALID_FD(fd)) EXECUTE_RESTORE_ERROR(close(fd));
return isOK;
}
BOOL CFileMapping::Map(FD fd, SIZE_T dwSize, SIZE_T dwOffset, int iProtected, int iFlag)
{
CHECK_ERROR(!IsValid(), ERROR_INVALID_STATE);
if(IS_INVALID_FD(fd))
{
CHECK_EINVAL((iFlag & MAP_ANONYMOUS) && (dwSize > 0));
}
else
{
CHECK_EINVAL((iFlag & MAP_ANONYMOUS) == 0);
struct stat st;
CHECK_ERROR_INVOKE(fstat(fd, &st));
CHECK_ERROR(S_ISREG(st.st_mode), ERROR_BAD_FILE_TYPE);
if(dwSize == 0)
dwSize = st.st_size;
}
m_pv = (PBYTE)mmap(nullptr, dwSize, iProtected, iFlag, fd, dwOffset);
if(IsValid())
{
m_dwSize = dwSize;
return TRUE;
}
return FALSE;
}
BOOL CFileMapping::Unmap()
{
CHECK_ERROR(IsValid(), ERROR_INVALID_STATE);
if(IS_NO_ERROR(munmap(m_pv, m_dwSize)))
{
m_pv = INVALID_MAP_ADDR;
m_dwSize = 0;
return TRUE;
}
return FALSE;
}
BOOL CFileMapping::MSync(int iFlag, SIZE_T dwSize)
{
CHECK_ERROR(IsValid(), ERROR_INVALID_STATE);
if(dwSize == 0) dwSize = m_dwSize;
return IS_NO_ERROR(msync(m_pv, dwSize, iFlag));
}

123
common/FileHelper.h Normal file
View File

@@ -0,0 +1,123 @@
/*
* Copyright: JessMA Open Source (ldcsaa@gmail.com)
*
* Author : Bruce Liang
* Website : https://github.com/ldcsaa
* Project : https://github.com/ldcsaa/HP-Socket
* Blog : http://www.cnblogs.com/ldcsaa
* Wiki : http://www.oschina.net/p/hp-socket
* QQ Group : 44636872, 75375912
*
* Licensed 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.
*/
#pragma once
#include "FuncHelper.h"
#include "StringT.h"
#include <unistd.h>
#include <sys/uio.h>
#include <sys/mman.h>
#define INVALID_MAP_ADDR ((PBYTE)(MAP_FAILED))
CString GetCurrentDirectory();
CString GetModuleFileName(pid_t pid = 0);
BOOL SetCurrentPathToModulePath(pid_t pid = 0);
class CFile
{
public:
BOOL Open(LPCTSTR lpszFilePath, int iFlag, mode_t iMode = 0);
BOOL Close();
BOOL Stat(struct stat& st);
BOOL GetSize(SIZE_T& dwSize);
SSIZE_T Read(PVOID pBuffer, SIZE_T dwCount)
{return read(m_fd, pBuffer, dwCount);}
SSIZE_T Write(PVOID pBuffer, SIZE_T dwCount)
{return write(m_fd, pBuffer, dwCount);}
SSIZE_T PRead(PVOID pBuffer, SIZE_T dwCount, SIZE_T dwOffset)
{return pread(m_fd, pBuffer, dwCount, dwOffset);}
SSIZE_T PWrite(PVOID pBuffer, SIZE_T dwCount, SIZE_T dwOffset)
{return pwrite(m_fd, pBuffer, dwCount, dwOffset);}
SSIZE_T ReadV(const iovec* pVec, int iVecCount)
{return readv(m_fd, pVec, iVecCount);}
SSIZE_T WriteV(const iovec* pVec, int iVecCount)
{return writev(m_fd, pVec, iVecCount);}
SSIZE_T Seek(SSIZE_T lOffset, int iWhence)
{return lseek(m_fd, lOffset, iWhence);}
BOOL IsValid() {return IS_VALID_FD(m_fd);}
operator FD () {return m_fd;}
BOOL IsExist() {return IsValid();}
BOOL IsDirectory();
BOOL IsFile();
static BOOL IsExist(LPCTSTR lpszFilePath);
static BOOL IsDirectory(LPCTSTR lpszFilePath);
static BOOL IsFile(LPCTSTR lpszFilePath);
static BOOL IsLink(LPCTSTR lpszFilePath);
public:
CFile(LPCTSTR lpszFilePath = nullptr, int iFlag = O_RDONLY, mode_t iMode = 0)
: m_fd(INVALID_FD)
{
if(lpszFilePath != nullptr)
Open(lpszFilePath, iFlag, iMode);
}
~CFile()
{
if(IsValid())
Close();
}
private:
FD m_fd;
};
class CFileMapping
{
public:
BOOL Map(LPCTSTR lpszFilePath, SIZE_T dwSize = 0, SIZE_T dwOffset = 0, int iProtected = PROT_READ, int iFlag = MAP_PRIVATE);
BOOL Map(FD fd, SIZE_T dwSize = 0, SIZE_T dwOffset = 0, int iProtected = PROT_READ, int iFlag = MAP_PRIVATE);
BOOL Unmap();
BOOL MSync(int iFlag = MS_SYNC, SIZE_T dwSize = 0);
BOOL IsValid () {return m_pv != INVALID_MAP_ADDR;}
SIZE_T Size () {return m_dwSize;}
LPBYTE Ptr () {return m_pv;}
operator LPBYTE () {return Ptr();}
public:
CFileMapping()
: m_pv(INVALID_MAP_ADDR)
, m_dwSize(0)
{
}
~CFileMapping()
{
if(IsValid())
Unmap();
}
private:
PBYTE m_pv;
SIZE_T m_dwSize;
};

393
common/FuncHelper.cpp Normal file
View File

@@ -0,0 +1,393 @@
/*
* Copyright: JessMA Open Source (ldcsaa@gmail.com)
*
* Author : Bruce Liang
* Website : https://github.com/ldcsaa
* Project : https://github.com/ldcsaa/HP-Socket
* Blog : http://www.cnblogs.com/ldcsaa
* Wiki : http://www.oschina.net/p/hp-socket
* QQ Group : 44636872, 75375912
*
* Licensed 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 "FuncHelper.h"
#include "Thread.h"
#include <ctype.h>
#include <sched.h>
#include <sys/time.h>
#include <sys/select.h>
#include <sys/timerfd.h>
#if !defined(__ANDROID__)
#include <sys/timeb.h>
#include <execinfo.h>
#ifdef __GNUC__
#include <cxxabi.h>
#endif
#endif
INT WaitFor(DWORD dwMillSecond, DWORD dwSecond, BOOL bExceptThreadInterrupted)
{
timeval tv {(time_t)dwSecond, (suseconds_t)(dwMillSecond * 1000)};
if(bExceptThreadInterrupted)
return NO_EINTR_EXCEPT_THR_INTR_INT(select(0, nullptr, nullptr, nullptr, &tv));
return NO_EINTR_INT(select(0, nullptr, nullptr, nullptr, &tv));
}
INT Sleep(DWORD dwMillSecond, DWORD dwSecond, BOOL bExceptThreadInterrupted)
{
timespec ts_req = {(time_t)dwSecond, (long)(dwMillSecond * 1000000)};
timespec ts_rem = ts_req;
INT rs = NO_ERROR;
while(IS_HAS_ERROR(rs = nanosleep(&ts_req, &ts_rem)))
{
if(!IS_INTR_ERROR())
break;
else
{
if(bExceptThreadInterrupted && ::IsThreadInterrupted())
break;
}
ts_req = ts_rem;
}
return rs;
}
__time64_t _time64(time_t* ptm)
{
return (__time64_t)time(ptm);
}
__time64_t _mkgmtime64(tm* ptm)
{
return (__time64_t)timegm(ptm);
}
tm* _gmtime64(tm* ptm, __time64_t* pt)
{
time_t t = (time_t)(*pt);
return gmtime_r(&t, ptm);
}
DWORD TimeGetTime()
{
return (DWORD)TimeGetTime64();
}
ULLONG TimeGetTime64()
{
#if !defined(__ANDROID__)
timeb tb;
if(ftime(&tb) == NO_ERROR)
return (((ULLONG)(tb.time)) * 1000 + tb.millitm);
#else
timespec ts;
if(clock_gettime(CLOCK_MONOTONIC, &ts) == NO_ERROR)
return (((ULLONG)(ts.tv_sec)) * 1000 + ts.tv_nsec / 1000000);
#endif
return 0ull;
}
DWORD GetTimeGap32(DWORD dwOriginal, DWORD dwCurrent)
{
if(dwCurrent == 0)
dwCurrent = ::TimeGetTime();
return dwCurrent - dwOriginal;
}
ULLONG GetTimeGap64(ULLONG ullOriginal, ULONGLONG ullCurrent)
{
if(ullCurrent == 0)
ullCurrent = ::TimeGetTime64();
return ullCurrent - ullOriginal;
}
LLONG TimevalToMillisecond(const timeval& tv)
{
return tv.tv_sec * 1000 + tv.tv_usec / 1000;
}
timeval& MillisecondToTimeval(LLONG ms, timeval& tv)
{
tv.tv_sec = (time_t)(ms / 1000);
tv.tv_usec = (suseconds_t)((ms % 1000) * 1000);
return tv;
}
LLONG TimespecToMillisecond(const timespec& ts)
{
return ts.tv_sec * 1000 + ts.tv_nsec / 1000000;
}
timespec& MillisecondToTimespec(LLONG ms, timespec& ts)
{
ts.tv_sec = (time_t)(ms / 1000);
ts.tv_nsec = (long)((ms % 1000) * 1000000);
return ts;
}
timeval& GetFutureTimeval(LLONG ms, timeval& tv, struct timezone* ptz)
{
gettimeofday(&tv, ptz);
tv.tv_sec += (time_t)(ms / 1000);
tv.tv_usec += (suseconds_t)((ms % 1000) * 1000);
return tv;
}
timespec& GetFutureTimespec(LLONG ms, timespec& ts, clockid_t clkid)
{
clock_gettime(clkid, &ts);
ts.tv_sec += (time_t)(ms / 1000);
ts.tv_nsec += (long)((ms % 1000) * 1000000);
return ts;
}
FD CreateTimer(LLONG llInterval, LLONG llStart, BOOL bRealTimeClock)
{
ASSERT_CHECK_EINVAL(llInterval >= 0L);
if(llStart < 0)
llStart = llInterval;
FD fdTimer = timerfd_create((bRealTimeClock ? CLOCK_REALTIME : CLOCK_MONOTONIC), TFD_NONBLOCK | TFD_CLOEXEC);
itimerspec its;
::MillisecondToTimespec(llStart, its.it_value);
::MillisecondToTimespec(llInterval, its.it_interval);
if(IS_HAS_ERROR(timerfd_settime(fdTimer, 0, &its, nullptr)))
{
close(fdTimer);
fdTimer = INVALID_FD;
}
return fdTimer;
}
BOOL ReadTimer(FD tmr, ULLONG* pVal, BOOL* pRs)
{
static const SSIZE_T SIZE = sizeof(ULLONG);
if(pVal == nullptr)
pVal = CreateLocalObject(ULLONG);
if(pRs == nullptr)
pRs = CreateLocalObject(BOOL);
if(read(tmr, pVal, SIZE) == SIZE)
*pRs = TRUE;
else
{
*pRs = FALSE;
if(!IS_WOULDBLOCK_ERROR())
return FALSE;
}
return TRUE;
}
BOOL fcntl_SETFL(FD fd, INT fl, BOOL bSet)
{
int val = fcntl(fd, F_GETFL);
if(IS_HAS_ERROR(val))
return FALSE;
val = bSet ? (val | fl) : (val & (~fl));
return IS_NO_ERROR(fcntl(fd, F_SETFL , val));
}
void PrintStackTrace()
{
#if !defined(__ANDROID__)
const int MAX_SIZE = 51;
void* arr[MAX_SIZE];
int size = backtrace(arr, MAX_SIZE);
char** messages = backtrace_symbols(arr, size);
for(int i = 1; i < size && messages != nullptr; i++)
{
char* mangled_name = nullptr;
char* offset_end = nullptr;
const char* offset_begin = nullptr;
for(char* p = messages[i]; *p; ++p)
{
if(*p == '(')
{
mangled_name = p;
}
else if(*p == '+')
{
offset_begin = p;
}
else if(*p == ')')
{
offset_end = p;
break;
}
}
if(mangled_name && offset_end && mangled_name < offset_end)
{
*mangled_name++ = 0;
*offset_end++ = 0;
if(offset_begin == nullptr)
offset_begin = "";
else
*(char*)offset_begin++ = 0;
while(*offset_end == ' ')
++offset_end;
#ifdef __GNUC__
int status;
char* real_name = abi::__cxa_demangle(mangled_name, nullptr, nullptr, &status);
if(status == 0)
FPRINTLN(stderr, " -> [%02d] %s : (%s+%s) %s", i, messages[i], real_name, offset_begin, offset_end);
else
FPRINTLN(stderr, " -> [%02d] %s : (%s+%s) %s", i, messages[i], mangled_name, offset_begin, offset_end);
if(real_name != nullptr)
free(real_name);
#else
FPRINTLN(stderr, " -> [%02d] %s : (%s+%s) %s", i, messages[i], mangled_name, offset_begin, offset_end);
#endif
}
else
{
FPRINTLN(stderr, " -> [%02d] %s", i, messages[i]);
}
}
free(messages);
#endif
}
void __EXIT_FN_(void (*fn)(int), LPCSTR lpszFnName, int* lpiExitCode, int iErrno, LPCSTR lpszFile, int iLine, LPCSTR lpszFunc, LPCSTR lpszTitle)
{
if(iErrno >= 0)
SetLastError(iErrno);
else
iErrno = GetLastError();
if(!lpszTitle)
{
lpszTitle = CreateLocalObjects(char, 64);
if(lpiExitCode)
sprintf((LPSTR)lpszTitle, "(#%d, 0x%zX) > %s(%d) [%d]", SELF_PROCESS_ID, (SIZE_T)SELF_THREAD_ID, lpszFnName, *lpiExitCode, iErrno);
else
sprintf((LPSTR)lpszTitle, "(#%d, 0x%zX) > %s() [%d]", SELF_PROCESS_ID, (SIZE_T)SELF_THREAD_ID, lpszFnName, iErrno);
}
if(lpszFile && iLine > 0)
FPRINTLN(stderr, "%s : %s\n => %s (%d) : %s", lpszTitle, strerror(iErrno), lpszFile, iLine, lpszFunc ? lpszFunc : "");
else
FPRINTLN(stderr, "%s : %s", lpszTitle, strerror(iErrno));
if(lpiExitCode)
fn(*lpiExitCode);
else
((void (*)())fn)();
}
void EXIT(int iExitCode, int iErrno, LPCSTR lpszFile, int iLine, LPCSTR lpszFunc, LPCSTR lpszTitle)
{
__EXIT_FN_(exit, "exit", &iExitCode, iErrno, lpszFile, iLine, lpszFunc, lpszTitle);
}
void _EXIT(int iExitCode, int iErrno, LPCSTR lpszFile, int iLine, LPCSTR lpszFunc, LPCSTR lpszTitle)
{
__EXIT_FN_(_exit, "_exit", &iExitCode, iErrno, lpszFile, iLine, lpszFunc, lpszTitle);
}
void ABORT(int iErrno, LPCSTR lpszFile, int iLine, LPCSTR lpszFunc, LPCSTR lpszTitle)
{
__EXIT_FN_((void (*)(int))abort, "abort", nullptr, iErrno, lpszFile, iLine, lpszFunc, lpszTitle);
}
BOOL SetSequenceThreadName(THR_ID tid, LPCTSTR lpszPrefix, volatile UINT& vuiSeq)
{
UINT uiSequence = InterlockedIncrement(&vuiSeq);
return SetThreadName(tid, lpszPrefix, uiSequence);
}
BOOL SetThreadName(THR_ID tid, LPCTSTR lpszPrefix, UINT uiSequence)
{
int iMaxSeqLength = (int)(MAX_THREAD_NAME_LENGTH - lstrlen(lpszPrefix));
ASSERT(iMaxSeqLength > 0);
if(iMaxSeqLength <= 0)
{
::SetLastError(ERROR_OUT_OF_RANGE);
return FALSE;
}
ULONGLONG uiDiv = 1;
for(int i = 0; i < iMaxSeqLength; i++)
uiDiv *= 10;
uiSequence = (UINT)(uiSequence % uiDiv);
CString strName;
strName.Format(_T("%s%u"), lpszPrefix, uiSequence);
return SetThreadName(tid, strName);
}
BOOL SetThreadName(THR_ID tid, LPCTSTR lpszName)
{
ASSERT(lstrlen(lpszName) <= MAX_THREAD_NAME_LENGTH);
if(tid == 0)
tid = SELF_THREAD_ID;
int rs = pthread_setname_np(tid, CT2A(lpszName));
CHECK_ERROR_CODE(rs)
return TRUE;
}

426
common/FuncHelper.h Normal file
View File

@@ -0,0 +1,426 @@
/*
* Copyright: JessMA Open Source (ldcsaa@gmail.com)
*
* Author : Bruce Liang
* Website : https://github.com/ldcsaa
* Project : https://github.com/ldcsaa/HP-Socket
* Blog : http://www.cnblogs.com/ldcsaa
* Wiki : http://www.oschina.net/p/hp-socket
* QQ Group : 44636872, 75375912
*
* Licensed 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.
*/
#pragma once
#include "hpsocket/GlobalDef.h"
#include "hpsocket/GlobalErrno.h"
#include "SysHelper.h"
#include <stdlib.h>
#include <sysexits.h>
#include <stdio.h>
#include <wchar.h>
#include <time.h>
#include <errno.h>
#include <fcntl.h>
#include <malloc.h>
#include <alloca.h>
#include <assert.h>
#include <string.h>
#include <unistd.h>
#include <sys/time.h>
#include <atomic>
#include <utility>
using namespace std;
#if !defined(__ANDROID__)
typedef atomic_ulong atomic_tid;
#define FPRINTLN(fd, fmt, ...) fprintf((fd), fmt "\n", ##__VA_ARGS__)
#else
typedef atomic_long atomic_tid;
#if defined(stdout)
#undef stdout
#endif
#if defined(stderr)
#undef stderr
#endif
#define stdout nullptr
#define stderr nullptr
#define FPRINTLN(fd, fmt, ...) printf(fmt "\n", ##__VA_ARGS__)
#endif
#define PRINTLN(fmt, ...) FPRINTLN(stdout, fmt, ##__VA_ARGS__)
#if defined(DEBUG) && defined(DEBUG_TRACE)
#define TRACE(fmt, ...) PRINTLN("> TRC (0x%zX, %d) " fmt, (SIZE_T)SELF_THREAD_ID, SELF_NATIVE_THREAD_ID, ##__VA_ARGS__)
#define ASSERT(expr) ((expr) ? TRUE : (::PrintStackTrace(), assert(FALSE), FALSE))
#else
#define TRACE(fmt, ...)
#define ASSERT(expr) assert(expr)
#endif
#define VERIFY(expr) ((expr) ? TRUE : (::PrintStackTrace(), ERROR_ABORT2(ERROR_VERIFY_CHECK), FALSE))
#define ASSERT_IS_NO_ERROR(expr) ASSERT(IS_NO_ERROR(expr))
#define VERIFY_IS_NO_ERROR(expr) VERIFY(IS_NO_ERROR(expr))
#define ENSURE(expr) VERIFY(expr)
#define ENSURE_IS_NO_ERROR(expr) VERIFY_IS_NO_ERROR(expr)
#define TEMP_FAILURE_RETRY_INT(exp) ((int)TEMP_FAILURE_RETRY(exp))
#define NO_EINTR TEMP_FAILURE_RETRY
#define NO_EINTR_INT TEMP_FAILURE_RETRY_INT
#define CHECK_IS_OK(expr) {if(IS_NOT_OK(expr)) return FALSE;}
#define CHECK_ERROR_FD(fd) {if(IS_INVALID_FD(fd)) return FALSE;}
#define CHECK_ERROR_INVOKE(expr) {if(!IS_NO_ERROR(expr)) return FALSE;}
#define CHECK_ERROR_CODE(rs) {if(!IS_NO_ERROR(rs)) {::SetLastError(rs); return FALSE;}}
#define CHECK_ERROR(expr, code) {if(!(expr)) {::SetLastError(code); return FALSE;}}
#define CHECK_EINVAL(expr) CHECK_ERROR(expr, ERROR_INVALID_PARAMETER)
#define ASSERT_CHECK_ERROR(expr, code) {ASSERT(expr); CHECK_ERROR(expr, code);}
#define ASSERT_CHECK_EINVAL(expr) {ASSERT(expr); CHECK_EINVAL(expr);}
#define SUCCEEDED(rs) IS_NO_ERROR(rs)
#define FAILED(rs) (!SUCCEEDED(rs))
#define IS_OK(rs) ((BOOL)(rs))
#define IS_NOT_OK(rs) (!IS_OK(rs))
#define IS_ERROR(code) (::GetLastError() == (code))
#define CONTINUE_IF_ERROR(code) {if(IS_ERROR(code)) continue;}
#define BREAK_IF_ERROR(code) {if(IS_ERROR(code)) break;}
#define IS_WOULDBLOCK_ERROR() IS_ERROR(ERROR_WOULDBLOCK)
#define CONTINUE_WOULDBLOCK_ERROR() CONTINUE_IF_ERROR(ERROR_WOULDBLOCK)
#define BREAK_WOULDBLOCK_ERROR() BREAK_IF_ERROR(ERROR_WOULDBLOCK)
#define IS_IO_PENDING_ERROR() IS_ERROR(ERROR_IO_PENDING)
#define CONTINUE_IO_PENDING_ERROR() CONTINUE_IF_ERROR(ERROR_IO_PENDING)
#define BREAK_IO_PENDING_ERROR() BREAK_IF_ERROR(ERROR_IO_PENDING)
#define IS_INTR_ERROR() IS_ERROR(ERROR_INTR)
#define CONTINUE_INTR_ERROR() CONTINUE_IF_ERROR(ERROR_INTR)
#define BREAK_INTR_ERROR() BREAK_IF_ERROR(ERROR_INTR)
#define EqualMemory(dest, src, len) (!memcmp((dest), (src), (len)))
#define MoveMemory(dest, src, len) memmove((dest), (src), (len))
#define CopyMemory(dest, src, len) memcpy((dest), (src), (len))
#define FillMemory(dest, len, ch) memset((dest), (ch), (len))
#define ZeroMemory(dest, len) FillMemory((dest), (len), 0)
#define ZeroObject(obj) ZeroMemory((&(obj)), sizeof(obj))
inline void SetLastError(int code) {errno = code;}
inline int GetLastError() {return errno;}
inline LPCSTR GetErrorStr(int code) {return strerror(code);}
inline LPCSTR GetLastErrorStr() {return GetErrorStr(errno);}
inline void PrintError(LPCSTR subject) {perror(subject);}
#define EXECUTE_RESET_ERROR(expr) (::SetLastError(0), (expr))
#define EXECUTE_RESTORE_ERROR(expr) {int __le_ = ::GetLastError(); (expr); ::SetLastError(__le_);}
#define EXECUTE_RESTORE_ERROR_RT(T, expr)\
({int __le_ = ::GetLastError(); T __rs_ = (expr); ::SetLastError(__le_); __rs_;})
#define ENSURE_ERROR(def_code) ({int __le_ = ::GetLastError(); if(__le_ == NO_ERROR) __le_ = (def_code); __le_;})
#define ENSURE_ERROR_CANCELLED ENSURE_ERROR(ERROR_CANCELLED)
#define TRIGGER(expr) EXECUTE_RESET_ERROR((expr))
#define _msize(p) malloc_usable_size(p)
#define CreateLocalObjects(T, n) ((T*)alloca(sizeof(T) * (n)))
#define CreateLocalObject(T) CreateLocalObjects(T, 1)
#define CallocObjects(T, n) ((T*)calloc((n), sizeof(T)))
#define MALLOC(T, n) ((T*)malloc(sizeof(T) * (n)))
#define REALLOC(T, p, n) ((T*)realloc((PVOID)(p), sizeof(T) * (n)))
#define FREE(p) free((PVOID)(p))
#define CALLOC(n, s) calloc((n), (s))
#define InterlockedExchangeAdd(p, n) __atomic_fetch_add((p), (n), memory_order_seq_cst)
#define InterlockedExchangeSub(p, n) __atomic_fetch_sub((p), (n), memory_order_seq_cst)
#define InterlockedAdd(p, n) __atomic_add_fetch((p), (n), memory_order_seq_cst)
#define InterlockedSub(p, n) __atomic_sub_fetch((p), (n), memory_order_seq_cst)
#define InterlockedIncrement(p) InterlockedAdd((p), 1)
#define InterlockedDecrement(p) InterlockedSub((p), 1)
#define ERROR_EXIT2(code, err) EXIT((code), (err), __FILE__, __LINE__, __PRETTY_FUNCTION__)
#define ERROR__EXIT2(code, err) _EXIT((code), (err), __FILE__, __LINE__, __PRETTY_FUNCTION__)
#define ERROR_ABORT2(err) ABORT((err), __FILE__, __LINE__, __PRETTY_FUNCTION__)
#define ERROR_EXIT(code) ERROR_EXIT2((code), -1)
#define ERROR__EXIT(code) ERROR__EXIT2((code), -1)
#define ERROR_ABORT() ERROR_ABORT2(-1)
#define IS_VALID_FD(fd) ((fd) != INVALID_FD)
#define IS_INVALID_FD(fd) (!IS_VALID_FD(fd))
#define IS_VALID_PVOID(pv) ((pv) != INVALID_PVOID)
#define IS_INVALID_PVOID(pv) (!IS_VALID_PVOID(pv))
#define TO_PVOID(v) ((PVOID)(UINT_PTR)(v))
#define FROM_PVOID(T, pv) ((T)(UINT_PTR)(pv))
#define IS_NULL(v) ((v) == nullptr)
#define IS_NOT_NULL(v) (!IS_NULL(v))
#define stricmp strcasecmp
#define strnicmp strncasecmp
#define wcsicmp wcscasecmp
#define wcsnicmp wcsncasecmp
#ifdef _UNICODE
#define tstrchr wcschr
#define tstrrchr wcsrchr
#define tstrstr wcsstr
#define tstrpbrk wcspbrk
#define tstrtok wcstok
#define stscanf swscanf
#define tstrlen wcslen
#define tstrcpy wcscpy
#define tstrcmp wcscmp
#define tstricmp wcsicmp
#define tstrncpy wcsncpy
#define tstrncmp wcsncmp
#define tstrnicmp wcsnicmp
#define tstrspn wcsspn
#define tstrcspn wcscspn
#define wsprintf swprintf
#else
#define tstrchr strchr
#define tstrrchr strrchr
#define tstrstr strstr
#define tstrpbrk strpbrk
#define tstrtok strtok_r
#define stscanf sscanf
#define tstrlen strlen
#define tstrcpy strcpy
#define tstrcmp strcmp
#define tstricmp stricmp
#define tstrncpy strncpy
#define tstrncmp strncmp
#define tstrnicmp strnicmp
#define tstrspn strspn
#define tstrcspn strcspn
#define wsprintf sprintf
#endif
inline const char* StrChr(const char* s, char c) {return strchr(s, c);}
inline const char* StrRChr(const char* s, char c) {return strrchr(s, c);}
inline const char* StrStr(const char* h, const char* n) {return strstr(h, n);}
inline const char* StrPBrk(const char* s, const char* a) {return strpbrk(s, a);}
inline const wchar_t* StrChr(const wchar_t* s, wchar_t c) {return wcschr(s, c);}
inline const wchar_t* StrRChr(const wchar_t* s, wchar_t c) {return wcsrchr(s, c);}
inline const wchar_t* StrStr(const wchar_t* h, const wchar_t* n) {return wcsstr(h, n);}
inline const wchar_t* StrPBrk(const wchar_t* s, const wchar_t* a) {return wcspbrk(s, a);}
inline LPSTR StrSep2(LPSTR* lpStr, LPCSTR lpDelim = " \t\r\n") {LPSTR lpTok; while((lpTok = strsep(lpStr, lpDelim)) != nullptr && lpTok[0] == 0); return lpTok;}
inline LPSTR TrimLeft(LPSTR* lpStr, LPCSTR lpDelim = " \t\r\n") {while((*lpStr)[0] != 0 && ::StrChr(lpDelim, (*lpStr)[0]) != nullptr) ++(*lpStr); return (*lpStr);}
inline LPSTR TrimRitht(LPSTR* lpStr, LPCSTR lpDelim = " \t\r\n")
{
LPSTR lpEnd = (*lpStr) + strlen(*lpStr) - 1;
LPSTR lpCur = lpEnd;
while(lpCur >= (*lpStr) && ::StrChr(lpDelim, lpCur[0]) != nullptr)
--lpCur;
if(lpCur != lpEnd)
lpCur[1] = 0;
return (*lpStr);
}
inline BOOL IsStrEmptyA(LPCSTR lpsz) {return (lpsz == nullptr || lpsz[0] == 0);}
inline BOOL IsStrEmptyW(LPCWSTR lpsz) {return (lpsz == nullptr || lpsz[0] == 0);}
inline BOOL IsStrNotEmptyA(LPCSTR lpsz) {return !IsStrEmptyA(lpsz);}
inline BOOL IsStrNotEmptyW(LPCWSTR lpsz){return !IsStrEmptyW(lpsz);}
inline LPCSTR SafeStrA(LPCSTR lpsz) {return (lpsz != nullptr) ? lpsz : "";}
inline LPCWSTR SafeStrW(LPCWSTR lpsz) {return (lpsz != nullptr) ? lpsz : L"";}
#ifdef _UNICODE
#define IsStrEmpty(lpsz) IsStrEmptyW(lpsz)
#define IsStrNotEmpty(lpsz) IsStrNotEmptyW(lpsz)
#define SafeStr(lpsz) SafeStrW(lpsz)
#else
#define IsStrEmpty(lpsz) IsStrEmptyA(lpsz)
#define IsStrNotEmpty(lpsz) IsStrNotEmptyA(lpsz)
#define SafeStr(lpsz) SafeStrA(lpsz)
#endif
inline int lstrlen(LPCTSTR p) {return (int)tstrlen(p);}
inline LPTSTR lstrcpy(LPTSTR d, LPCTSTR s) {return tstrcpy(d, s);}
inline LPTSTR lstrncpy(LPTSTR d, LPCTSTR s, size_t n) {return tstrncpy(d, s, n);}
inline int lstrcmp(LPCTSTR s1, LPCTSTR s2) {return tstrcmp(s1, s2);}
inline int lstrncmp(LPCTSTR s1, LPCTSTR s2, size_t n) {return tstrncmp(s1, s2, n);}
inline int lstricmp(LPCTSTR s1, LPCTSTR s2) {return tstricmp(s1, s2);}
inline int lstrnicmp(LPCTSTR s1, LPCTSTR s2, size_t n) {return tstrnicmp(s1, s2, n);}
inline int lstrspn(LPCTSTR s, LPCTSTR accept) {return (int)tstrspn(s, accept);}
inline int lstrcspn(LPCTSTR s, LPCTSTR accept) {return (int)tstrcspn(s, accept);}
template <typename T, size_t N> char (&_ArraySizeHelper(T(&arr)[N]))[N];
template <typename T, size_t N> char (&_ArraySizeHelper(const T(&arr)[N]))[N];
#define ARRAY_SIZE(arr) (sizeof(_ArraySizeHelper(arr)))
#ifndef _countof
#define _countof(arr) ARRAY_SIZE(arr)
#endif
#ifndef __countof
#define __countof(arr) ARRAY_SIZE(arr)
#endif
#define THREAD_YIELD_CYCLE 63
#define THREAD_SWITCH_CYCLE 4095
inline void YieldThread(UINT i = THREAD_YIELD_CYCLE)
{
if((i & THREAD_SWITCH_CYCLE) == THREAD_SWITCH_CYCLE)
::SwitchToThread();
else if((i & THREAD_YIELD_CYCLE) == THREAD_YIELD_CYCLE)
::YieldProcessor();
}
INT WaitFor(DWORD dwMillSecond, DWORD dwSecond = 0, BOOL bExceptThreadInterrupted = FALSE);
INT Sleep(DWORD dwMillSecond, DWORD dwSecond = 0, BOOL bExceptThreadInterrupted = FALSE);
__time64_t _time64(time_t* ptm = nullptr);
__time64_t _mkgmtime64(tm* ptm);
tm* _gmtime64(tm* ptm, __time64_t* pt);
DWORD TimeGetTime();
ULLONG TimeGetTime64();
DWORD GetTimeGap32(DWORD dwOriginal, DWORD dwCurrent = 0);
ULLONG GetTimeGap64(ULLONG ullOriginal, ULONGLONG ullCurrent = 0);
LLONG TimevalToMillisecond(const timeval& tv);
timeval& MillisecondToTimeval(LLONG ms, timeval& tv);
LLONG TimespecToMillisecond(const timespec& ts);
timespec& MillisecondToTimespec(LLONG ms, timespec& ts);
timeval& GetFutureTimeval(LLONG ms, timeval& tv, struct timezone* ptz = nullptr);
timespec& GetFutureTimespec(LLONG ms, timespec& ts, clockid_t clkid = CLOCK_MONOTONIC);
FD CreateTimer(LLONG llInterval, LLONG llStart = -1, BOOL bRealTimeClock = FALSE);
BOOL ReadTimer(FD tmr, ULLONG* pVal = nullptr, BOOL* pRs = nullptr);
BOOL fcntl_SETFL(FD fd, INT fl, BOOL bSet = TRUE);
void PrintStackTrace();
void EXIT(int iExitCode = 0, int iErrno = -1, LPCSTR lpszFile = nullptr, int iLine = 0, LPCSTR lpszFunc = nullptr, LPCSTR lpszTitle = nullptr);
void _EXIT(int iExitCode = 0, int iErrno = -1, LPCSTR lpszFile = nullptr, int iLine = 0, LPCSTR lpszFunc = nullptr, LPCSTR lpszTitle = nullptr);
void ABORT(int iErrno = -1, LPCSTR lpszFile = nullptr, int iLine = 0, LPCSTR lpszFunc = nullptr, LPCSTR lpszTitle = nullptr);
/* ¹¤×÷Ïß³ÌÃû³Æ×î´ó³¤¶È */
#define MAX_THREAD_NAME_LENGTH 15
BOOL SetSequenceThreadName(THR_ID tid, LPCTSTR lpszPrefix, volatile UINT& vuiSeq);
BOOL SetThreadName(THR_ID tid, LPCTSTR lpszPrefix, UINT uiSequence);
BOOL SetThreadName(THR_ID tid, LPCTSTR lpszName);
template<typename T, typename = enable_if_t<is_integral<T>::value>>
inline bool IS_INFINITE(T v)
{
return v == (T)INFINITE;
}
template<typename T, typename = enable_if_t<is_integral<T>::value>>
inline bool IS_HAS_ERROR(T v)
{
return v == (T)HAS_ERROR;
}
template<typename T, typename = enable_if_t<is_integral<T>::value>>
inline bool IS_NO_ERROR(T v)
{
return v == (T)NO_ERROR;
}
template<typename T>
inline T InterlockedCompareExchange(volatile T* _Tgt, T _Value, T _Exp, BOOL _bWeek = FALSE, memory_order m1 = memory_order_seq_cst, memory_order m2 = memory_order_seq_cst)
{
__atomic_compare_exchange_n(_Tgt, &_Exp, _Value, _bWeek, m1, m2);
return _Exp;
}
template<typename T, typename V, typename E, typename = enable_if_t<is_same<decay_t<T>, decay_t<V>>::value && is_same<decay_t<V>, decay_t<E>>::value>>
inline V* InterlockedCompareExchangePointer(volatile T** _Tgt, V* _Value, E* _Exp, BOOL _bWeek = FALSE, memory_order m1 = memory_order_seq_cst, memory_order m2 = memory_order_seq_cst)
{
return (V*)(ULONG_PTR)InterlockedCompareExchange((volatile ULONG_PTR*)(volatile PVOID*)_Tgt, (ULONG_PTR)(PVOID)_Value, (ULONG_PTR)(PVOID)_Exp, _bWeek, m1, m2);
}
template<typename T, typename ... A>
inline T* ConstructObject(T* p, A&& ... args)
{
return new (p) T(forward<A>(args) ...);
}
template<typename T>
inline void DestructObject(T* p)
{
p->T::~T();
}
template<typename T1, typename T2, typename = enable_if_t<is_same<decay_t<T1>, decay_t<T2>>::value>>
inline void CopyPlainObject(T1* p1, const T2* p2)
{
CopyMemory(p1, p2, sizeof(T1));
}
template<typename T, typename C, typename = enable_if_t<is_integral<T>::value && (is_same<C, char>::value || is_same<C, wchar_t>::value)>>
C* _n_2_c(T value, C* lpszDest, int radix)
{
static const C* dig = "0123456789abcdefghijklmnopqrstuvwxyz";
bool neg = false;
if(is_signed<T>::value && value < 0)
{
value = -value;
neg = true;
}
int n = 0;
do
{
lpszDest[n++] = dig[value % radix];
value /= radix;
} while(value);
if(neg) lpszDest[n++] = '-';
lpszDest[n] = 0;
C c, *p, *q;
for(p = lpszDest, q = p + n - 1; p < q; ++p, --q)
c = *p, *p = *q, *q = c;
return lpszDest;
}
#define itoa(v, p, r) _n_2_c<INT, char>((v), (p), (r))
#define ltoa(v, p, r) _n_2_c<LONG, char>((v), (p), (r))
#define lltoa(v, p, r) _n_2_c<LLONG, char>((v), (p), (r))
#define uitoa(v, p, r) _n_2_c<UINT, char>((v), (p), (r))
#define ultoa(v, p, r) _n_2_c<ULONG, char>((v), (p), (r))
#define ulltoa(v, p, r) _n_2_c<ULLONG, char>((v), (p), (r))
#define itow(v, p, r) _n_2_c<INT, wchar_t>((v), (p), (r))
#define ltow(v, p, r) _n_2_c<LONG, wchar_t>((v), (p), (r))
#define lltow(v, p, r) _n_2_c<LLONG, wchar_t>((v), (p), (r))
#define uitow(v, p, r) _n_2_c<UINT, wchar_t>((v), (p), (r))
#define ultow(v, p, r) _n_2_c<ULONG, wchar_t>((v), (p), (r))
#define ulltow(v, p, r) _n_2_c<ULLONG, wchar_t>((v), (p), (r))
#define HEX_CHAR_TO_VALUE(c) (c <= '9' ? c - '0' : (c <= 'F' ? c - 'A' + 0x0A : c - 'a' + 0X0A))
#define HEX_DOUBLE_CHAR_TO_VALUE(pc) ((BYTE)(((HEX_CHAR_TO_VALUE(*(pc))) << 4) | (HEX_CHAR_TO_VALUE(*((pc) + 1)))))
#define HEX_VALUE_TO_CHAR(n) (n <= 9 ? n + '0' : (n <= 'F' ? n + 'A' - 0X0A : n + 'a' - 0X0A))
#define HEX_VALUE_TO_DOUBLE_CHAR(pc, n) {*(pc) = (BYTE)HEX_VALUE_TO_CHAR((n >> 4)); *((pc) + 1) = (BYTE)HEX_VALUE_TO_CHAR((n & 0X0F));}

40
common/GeneralHelper.h Normal file
View File

@@ -0,0 +1,40 @@
/*
* Copyright: JessMA Open Source (ldcsaa@gmail.com)
*
* Author : Bruce Liang
* Website : https://github.com/ldcsaa
* Project : https://github.com/ldcsaa/HP-Socket
* Blog : http://www.cnblogs.com/ldcsaa
* Wiki : http://www.oschina.net/p/hp-socket
* QQ Group : 44636872, 75375912
*
* Licensed 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.
*/
#pragma once
#include "hpsocket/GlobalDef.h"
#include "hpsocket/GlobalErrno.h"
#include "Singleton.h"
#include "STLHelper.h"
#include "FuncHelper.h"
#include "StringT.h"
#include "SysHelper.h"
#include "PrivateHeap.h"
#include "Semaphore.h"
#include "RWLock.h"
#include "BufferPtr.h"
#include "Event.h"
#include "CriSec.h"
#include "Thread.h"
#include "SignalHandler.h"

348
common/IODispatcher.cpp Normal file
View File

@@ -0,0 +1,348 @@
/*
* Copyright: JessMA Open Source (ldcsaa@gmail.com)
*
* Author : Bruce Liang
* Website : https://github.com/ldcsaa
* Project : https://github.com/ldcsaa/HP-Socket
* Blog : http://www.cnblogs.com/ldcsaa
* Wiki : http://www.oschina.net/p/hp-socket
* QQ Group : 44636872, 75375912
*
* Licensed 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 "IODispatcher.h"
#include "FuncHelper.h"
#include <signal.h>
#include <pthread.h>
volatile UINT CIODispatcher::sm_uiNum = MAXUINT;
LPCTSTR CIODispatcher::WORKER_THREAD_PREFIX = _T("io-disp-");
BOOL CIODispatcher::Start(IIOHandler* pHandler, int iWorkerMaxEvents, int iWorkers)
{
ASSERT_CHECK_EINVAL(pHandler && iWorkerMaxEvents >= 0 && iWorkers >= 0);
CHECK_ERROR(!HasStarted(), ERROR_INVALID_STATE);
if(iWorkerMaxEvents == 0) iWorkerMaxEvents = DEF_WORKER_MAX_EVENTS;
if(iWorkers == 0) iWorkers = DEFAULT_WORKER_THREAD_COUNT;
m_iMaxEvents = iWorkerMaxEvents;
m_iWorkers = iWorkers;
m_pHandler = pHandler;
m_evExit = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC | EFD_SEMAPHORE);
if(IS_INVALID_FD(m_evExit))
goto START_ERROR;
m_pContexts = make_unique<TDispContext[]>(m_iWorkers);
for(int i = 0; i < m_iWorkers; i++)
{
TDispContext& ctx = m_pContexts[i];
ctx.m_iIndex = i;
ctx.m_epoll = epoll_create1(EPOLL_CLOEXEC);
CHECK_ERROR_FD(ctx.m_epoll);
ctx.m_evCmd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);
if(IS_INVALID_FD(ctx.m_evCmd))
goto START_ERROR;
if(!VERIFY(AddFD(i, ctx.m_evCmd, EPOLLIN | EPOLLET, &ctx.m_evCmd)))
goto START_ERROR;
if(!VERIFY(AddFD(i, m_evExit, EPOLLIN, &m_evExit)))
goto START_ERROR;
sigset_t ss;
sigemptyset(&ss);
sigaddset(&ss, SIGPIPE);
VERIFY_IS_NO_ERROR(pthread_sigmask(SIG_BLOCK, &ss, nullptr));
ctx.m_pWorker = make_unique<CWorkerThread>();
if(!VERIFY(ctx.m_pWorker->Start(this, &CIODispatcher::WorkerProc, &ctx)))
goto START_ERROR;
}
return TRUE;
START_ERROR:
EXECUTE_RESTORE_ERROR(Stop(FALSE));
return FALSE;
}
BOOL CIODispatcher::Stop(BOOL bCheck)
{
if(bCheck) CHECK_ERROR(HasStarted(), ERROR_INVALID_STATE);
BOOL isOK = TRUE;
if(m_pContexts)
{
isOK &= IS_NO_ERROR(eventfd_write(m_evExit, m_iWorkers));
for(int i = 0; i < m_iWorkers; i++)
{
TDispContext& ctx = m_pContexts[i];
if(ctx.m_pWorker)
isOK &= ctx.m_pWorker->Join();
if(!ctx.m_queue.IsEmpty())
{
TDispCommand* pCmd = nullptr;
while(ctx.m_queue.PopFront(&pCmd))
TDispCommand::Destruct(pCmd);
VERIFY(ctx.m_queue.IsEmpty());
}
if(IS_VALID_FD(ctx.m_evCmd))
isOK &= IS_NO_ERROR(close(ctx.m_evCmd));
if(IS_VALID_FD(ctx.m_epoll))
isOK &= IS_NO_ERROR(close(ctx.m_epoll));
}
}
if(IS_VALID_FD(m_evExit))
isOK &= IS_NO_ERROR(close(m_evExit));
Reset();
return isOK;
}
VOID CIODispatcher::Reset()
{
m_uiSeq = MAXUINT;
m_iWorkers = 0;
m_iMaxEvents= 0;
m_evExit = INVALID_FD;
m_pHandler = nullptr;
m_pContexts = nullptr;
}
VOID CIODispatcher::MakePrefix()
{
UINT uiNumber = ::InterlockedIncrement(&sm_uiNum);
m_strPrefix.Format(_T("%s%u-"), WORKER_THREAD_PREFIX, uiNumber);
}
TDispContext& CIODispatcher::GetContext(int idx, FD fd)
{
if(idx < 0) idx = fd;
ASSERT(idx >= 0);
if(idx >= m_iWorkers) idx %= m_iWorkers;
return m_pContexts[idx];
}
BOOL CIODispatcher::SendCommandByIndex(int idx, USHORT t, UINT_PTR wp, UINT_PTR lp)
{
return SendCommandByIndex(idx, TDispCommand::Construct(t, wp, lp));
}
BOOL CIODispatcher::SendCommandByIndex(int idx, TDispCommand* pCmd)
{
TDispContext& ctx = GetContextByIndex(idx);
return SendCommand(ctx, pCmd);
}
BOOL CIODispatcher::SendCommandByFD(FD fd, USHORT t, UINT_PTR wp, UINT_PTR lp)
{
return SendCommandByFD(fd, TDispCommand::Construct(t, wp, lp));
}
BOOL CIODispatcher::SendCommandByFD(FD fd, TDispCommand* pCmd)
{
TDispContext& ctx = GetContextByFD(fd);
return SendCommand(ctx, pCmd);
}
BOOL CIODispatcher::SendCommand(TDispContext& ctx, TDispCommand* pCmd)
{
ctx.m_queue.PushBack(pCmd);
return VERIFY_IS_NO_ERROR(eventfd_write(ctx.m_evCmd, 1));
}
BOOL CIODispatcher::CtlFD(int idx, FD fd, int op, UINT mask, PVOID pv)
{
const TDispContext& ctx = GetContext(idx, fd);
epoll_event evt = {mask, pv};
return IS_NO_ERROR(epoll_ctl(ctx.m_epoll, op, fd, &evt));
}
int CIODispatcher::WorkerProc(TDispContext* pContext)
{
::SetSequenceThreadName(SELF_THREAD_ID, m_strPrefix, m_uiSeq);
m_pHandler->OnDispatchThreadStart(SELF_THREAD_ID);
BOOL bRun = TRUE;
unique_ptr<epoll_event[]> pEvents = make_unique<epoll_event[]>(m_iMaxEvents);
while(bRun)
{
int rs = NO_EINTR_INT(epoll_pwait(pContext->m_epoll, pEvents.get(), m_iMaxEvents, INFINITE, nullptr));
if(rs <= TIMEOUT)
ERROR_ABORT();
for(int i = 0; i < rs; i++)
{
UINT events = pEvents[i].events;
PVOID ptr = pEvents[i].data.ptr;
if(ptr == &pContext->m_evCmd)
ProcessCommand(pContext, events);
else if(ptr == &m_evExit)
bRun = ProcessExit(pContext, events);
else
ProcessIo(pContext, ptr, events);
}
}
m_pHandler->OnDispatchThreadEnd(SELF_THREAD_ID);
return 0;
}
BOOL CIODispatcher::ProcessCommand(TDispContext* pContext, UINT events)
{
if(events & _EPOLL_ALL_ERROR_EVENTS)
ERROR_ABORT();
if(!(events & EPOLLIN))
return FALSE;
BOOL isOK = TRUE;
eventfd_t v;
int rs = eventfd_read(pContext->m_evCmd, &v);
if(IS_NO_ERROR(rs))
{
ASSERT(v > 0);
TDispCommand* pCmd = nullptr;
while(pContext->m_queue.PopFront(&pCmd))
{
m_pHandler->OnCommand(pContext, pCmd);
TDispCommand::Destruct(pCmd);
}
}
else if(IS_HAS_ERROR(rs))
{
ASSERT(IS_WOULDBLOCK_ERROR());
isOK = FALSE;
}
return isOK;
}
BOOL CIODispatcher::ProcessExit(const TDispContext* pContext, UINT events)
{
if(events & _EPOLL_ALL_ERROR_EVENTS)
ERROR_ABORT();
if(!(events & EPOLLIN))
return TRUE;
BOOL bRun = TRUE;
eventfd_t v;
int rs = eventfd_read(m_evExit, &v);
if(IS_HAS_ERROR(rs))
ASSERT(IS_WOULDBLOCK_ERROR());
else
{
ASSERT(v == 1);
bRun = FALSE;
}
return bRun;
}
BOOL CIODispatcher::ProcessIo(const TDispContext* pContext, PVOID pv, UINT events)
{
if(!m_pHandler->OnBeforeProcessIo(pContext, pv, events))
return FALSE;
BOOL rs = DoProcessIo(pContext, pv, events);
m_pHandler->OnAfterProcessIo(pContext, pv, events, rs);
return rs;
}
BOOL CIODispatcher::DoProcessIo(const TDispContext* pContext, PVOID pv, UINT events)
{
if(events & EPOLLERR)
return m_pHandler->OnError(pContext, pv, events);
if((events & EPOLLPRI) && !m_pHandler->OnReadyPrivilege(pContext, pv, events))
return FALSE;
if((events & EPOLLIN) && !m_pHandler->OnReadyRead(pContext, pv, events))
return FALSE;
if((events & EPOLLOUT) && !m_pHandler->OnReadyWrite(pContext, pv, events))
return FALSE;
if((events & (_EPOLL_HUNGUP_EVENTS)) && !m_pHandler->OnHungUp(pContext, pv, events))
return FALSE;
return TRUE;
}
FD CIODispatcher::AddTimer(int idx, LLONG llInterval, PVOID pv)
{
FD fdTimer = ::CreateTimer(llInterval);
if(IS_VALID_FD(fdTimer))
{
if(!AddFD(idx, fdTimer, EPOLLIN | EPOLLET, pv))
{
close(fdTimer);
fdTimer = INVALID_FD;
}
}
return fdTimer;
}
BOOL CIODispatcher::DelTimer(int idx, FD fdTimer)
{
BOOL isOK = FALSE;
if(IS_VALID_FD(fdTimer))
{
if(DelFD(idx, fdTimer))
isOK = TRUE;
close(fdTimer);
}
return isOK;
}

272
common/IODispatcher.h Normal file
View File

@@ -0,0 +1,272 @@
/*
* Copyright: JessMA Open Source (ldcsaa@gmail.com)
*
* Author : Bruce Liang
* Website : https://github.com/ldcsaa
* Project : https://github.com/ldcsaa/HP-Socket
* Blog : http://www.cnblogs.com/ldcsaa
* Wiki : http://www.oschina.net/p/hp-socket
* QQ Group : 44636872, 75375912
*
* Licensed 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.
*/
#pragma once
#include "hpsocket/GlobalDef.h"
#include "Singleton.h"
#include "RingBuffer.h"
#include "Thread.h"
#include <sys/epoll.h>
#include <sys/eventfd.h>
#include <sys/timerfd.h>
#include <memory>
using namespace std;
#define _EPOLL_READ_PRI_EVENTS (EPOLLPRI | EPOLLRDHUP)
#define _EPOLL_READ_EVENTS (EPOLLIN | EPOLLRDHUP)
#define _EPOLL_ALL_READ_EVENTS (_EPOLL_READ_EVENTS | _EPOLL_READ_PRI_EVENTS)
#define _EPOLL_WRITE_EVENTS (EPOLLOUT)
#define _EPOLL_NORMAL_RW_EVENTS (_EPOLL_READ_EVENTS | _EPOLL_WRITE_EVENTS)
#define _EPOLL_ALL_RW_EVENTS (_EPOLL_ALL_READ_EVENTS | _EPOLL_WRITE_EVENTS)
#define _EPOLL_ERROR_EVENTS (EPOLLERR)
#define _EPOLL_HUNGUP_EVENTS (EPOLLHUP | EPOLLRDHUP)
#define _EPOLL_ALL_ERROR_EVENTS (_EPOLL_ERROR_EVENTS | _EPOLL_HUNGUP_EVENTS)
#define _EPOLL_ALL_NORMAL_EVENTS (_EPOLL_NORMAL_RW_EVENTS | _EPOLL_ALL_ERROR_EVENTS)
#define _EPOLL_ALL_EVENTS (_EPOLL_ALL_RW_EVENTS | _EPOLL_ALL_ERROR_EVENTS)
#define DISP_EVENT_FLAG_R 0x1
#define DISP_EVENT_FLAG_W 0x2
#define DISP_EVENT_FLAG_H 0x4
#define RETRIVE_EVENT_FLAG_R(evt) ((evt) & (_EPOLL_ALL_READ_EVENTS) ? DISP_EVENT_FLAG_R : 0)
#define RETRIVE_EVENT_FLAG_W(evt) ((evt) & (_EPOLL_WRITE_EVENTS) ? DISP_EVENT_FLAG_W : 0)
#define RETRIVE_EVENT_FLAG_RW(evt) (RETRIVE_EVENT_FLAG_R(evt) | RETRIVE_EVENT_FLAG_W(evt))
#define RETRIVE_EVENT_FLAG_H(evt) ((evt) & (_EPOLL_HUNGUP_EVENTS) ? DISP_EVENT_FLAG_H : 0)
#ifndef EPOLLEXCLUSIVE
#define EPOLLEXCLUSIVE (1u << 28)
#endif
#define MAYBE_EPOLLEXCLUSIVE (::IsKernelVersionAbove(4, 5, 0) ? EPOLLEXCLUSIVE : 0)
// ------------------------------------------------------------------------------------------------------------------------------------------------------- //
struct TDispCommand;
class CIODispatcher;
struct TDispContext
{
friend class CIODispatcher;
using CCommandQueue = CCASQueue<TDispCommand>;
using CWorkerThread = CThread<CIODispatcher, TDispContext, int>;
public:
int GetIndex() const {return m_iIndex;}
THR_ID GetThreadId() const {return m_pWorker != nullptr ? m_pWorker->GetThreadID() : 0;}
public:
TDispContext() {Reset();}
~TDispContext() = default;
DECLARE_NO_COPY_CLASS(TDispContext)
private:
VOID Reset()
{
m_iIndex = -1;
m_epoll = INVALID_FD;
m_evCmd = INVALID_FD;
m_pWorker = nullptr;
}
private:
int m_iIndex;
FD m_epoll;
FD m_evCmd;
CCommandQueue m_queue;
unique_ptr<CWorkerThread> m_pWorker;
};
struct TDispCommand
{
USHORT type;
UINT_PTR wParam;
UINT_PTR lParam;
static TDispCommand* Construct(USHORT t, UINT_PTR wp = 0, UINT_PTR lp = 0)
{return new TDispCommand(t, wp, lp);}
static VOID Destruct(TDispCommand* p)
{if(p) delete p;}
private:
TDispCommand(USHORT t, UINT_PTR wp = 0, UINT_PTR lp = 0)
: type(t), wParam(wp), lParam(lp)
{
}
~TDispCommand() = default;
};
// ------------------------------------------------------------------------------------------------------------------------------------------------------- //
class IIOHandler
{
public:
virtual VOID OnCommand(const TDispContext* pContext, TDispCommand* pCmd) = 0;
virtual BOOL OnBeforeProcessIo(const TDispContext* pContext, PVOID pv, UINT events) = 0;
virtual VOID OnAfterProcessIo(const TDispContext* pContext, PVOID pv, UINT events, BOOL rs) = 0;
virtual BOOL OnReadyRead(const TDispContext* pContext, PVOID pv, UINT events) = 0;
virtual BOOL OnReadyWrite(const TDispContext* pContext, PVOID pv, UINT events) = 0;
virtual BOOL OnHungUp(const TDispContext* pContext, PVOID pv, UINT events) = 0;
virtual BOOL OnError(const TDispContext* pContext, PVOID pv, UINT events) = 0;
virtual BOOL OnReadyPrivilege(const TDispContext* pContext, PVOID pv, UINT events) = 0;
virtual VOID OnDispatchThreadStart(THR_ID tid) = 0;
virtual VOID OnDispatchThreadEnd(THR_ID tid) = 0;
public:
virtual ~IIOHandler() = default;
};
class CIOHandler : public IIOHandler
{
public:
virtual VOID OnCommand(const TDispContext* pContext, TDispCommand* pCmd) override {}
virtual BOOL OnBeforeProcessIo(const TDispContext* pContext, PVOID pv, UINT events) override {return TRUE;}
virtual VOID OnAfterProcessIo(const TDispContext* pContext, PVOID pv, UINT events, BOOL rs) override {}
virtual BOOL OnReadyWrite(const TDispContext* pContext, PVOID pv, UINT events) override {return TRUE;}
virtual BOOL OnHungUp(const TDispContext* pContext, PVOID pv, UINT events) override {return TRUE;}
virtual BOOL OnError(const TDispContext* pContext, PVOID pv, UINT events) override {return TRUE;}
virtual BOOL OnReadyPrivilege(const TDispContext* pContext, PVOID pv, UINT events) override {return TRUE;}
virtual VOID OnDispatchThreadStart(THR_ID tid) override {}
virtual VOID OnDispatchThreadEnd(THR_ID tid) override {}
};
// ------------------------------------------------------------------------------------------------------------------------------------------------------- //
class CIODispatcher
{
public:
static const int DEF_WORKER_MAX_EVENTS = 64;
using CCommandQueue = TDispContext::CCommandQueue;
using CWorkerThread = TDispContext::CWorkerThread;
public:
BOOL Start(IIOHandler* pHandler, int iWorkerMaxEvents = DEF_WORKER_MAX_EVENTS, int iWorkers = 0);
BOOL Stop(BOOL bCheck = TRUE);
BOOL SendCommandByIndex(int idx, TDispCommand* pCmd);
BOOL SendCommandByIndex(int idx, USHORT t, UINT_PTR wp = 0, UINT_PTR lp = 0);
BOOL SendCommandByFD(FD fd, TDispCommand* pCmd);
BOOL SendCommandByFD(FD fd, USHORT t, UINT_PTR wp = 0, UINT_PTR lp = 0);
BOOL SendCommand(TDispContext& ctx, TDispCommand* pCmd);
template<class _List, typename = enable_if_t<is_same<remove_reference_t<typename _List::reference>, TDispCommand*>::value>>
BOOL SendCommandsByIndex(int idx, const _List& cmds)
{
TDispContext& ctx = GetContextByIndex(idx);
return SendCommands(ctx, cmds);
}
template<class _List, typename = enable_if_t<is_same<remove_reference_t<typename _List::reference>, TDispCommand*>::value>>
BOOL SendCommandsByFD(FD fd, const _List& cmds)
{
TDispContext& ctx = GetContextByFD(fd);
return SendCommands(ctx, cmds);
}
template<class _List, typename = enable_if_t<is_same<remove_reference_t<typename _List::reference>, TDispCommand*>::value>>
BOOL SendCommands(TDispContext& ctx, const _List& cmds)
{
size_t size = cmds.size();
if(size == 0) return FALSE;
for(auto it = cmds.begin(), end = cmds.end(); it != end; ++it)
ctx.m_queue.PushBack(*it);
return VERIFY_IS_NO_ERROR(eventfd_write(ctx.m_evCmd, size));
}
BOOL AddFD(int idx, FD fd, UINT mask, PVOID pv) {return CtlFD(idx, fd, EPOLL_CTL_ADD, mask, pv);}
BOOL ModFD(int idx, FD fd, UINT mask, PVOID pv) {return CtlFD(idx, fd, EPOLL_CTL_MOD, mask, pv);}
BOOL DelFD(int idx, FD fd) {return CtlFD(idx, fd, EPOLL_CTL_DEL, 0, nullptr);}
BOOL CtlFD(int idx, FD fd, int op, UINT mask, PVOID pv);
BOOL AddFD(FD fd, UINT mask, PVOID pv) {return CtlFD(-1, fd, EPOLL_CTL_ADD, mask, pv);}
BOOL ModFD(FD fd, UINT mask, PVOID pv) {return CtlFD(-1, fd, EPOLL_CTL_MOD, mask, pv);}
BOOL DelFD(FD fd) {return CtlFD(-1, fd, EPOLL_CTL_DEL, 0, nullptr);}
BOOL CtlFD(FD fd, int op, UINT mask, PVOID pv) {return CtlFD(-1, fd, op, mask, pv);}
BOOL ProcessIo(const TDispContext* pContext, PVOID pv, UINT events);
FD AddTimer (int idx, LLONG llInterval, PVOID pv);
BOOL DelTimer (int idx, FD fdTimer);
FD AddTimer (LLONG llInterval, PVOID pv) {return AddTimer(-1, llInterval, pv);}
BOOL DelTimer (FD fdTimer) {return DelTimer(-1, fdTimer);}
private:
int WorkerProc(TDispContext* pContext);
BOOL ProcessExit(const TDispContext* pContext, UINT events);
BOOL ProcessCommand(TDispContext* pContext, UINT events);
BOOL DoProcessIo(const TDispContext* pContext, PVOID pv, UINT events);
VOID Reset();
VOID MakePrefix();
TDispContext& GetContextByIndex(int idx) {return GetContext(idx, -1);}
TDispContext& GetContextByFD(FD fd) {return GetContext(-1, fd);}
TDispContext& GetContext(int idx, FD fd);
public:
const TDispContext& GetContextRefByIndex(int idx) {return GetContextByIndex(idx);}
const TDispContext& GetContextRefByFD(FD fd) {return GetContextByFD(fd);}
const TDispContext& GetContextRef(int idx, FD fd) {return GetContext(idx, fd);}
BOOL HasStarted() {return m_pHandler && m_pContexts;}
int GetWorkers() {return m_iWorkers;}
const TDispContext* GetContexts() {return m_pContexts.get();}
CIODispatcher() {MakePrefix(); Reset();}
~CIODispatcher() {if(HasStarted()) Stop();}
DECLARE_NO_COPY_CLASS(CIODispatcher)
private:
static LPCTSTR WORKER_THREAD_PREFIX;
static volatile UINT sm_uiNum;
volatile UINT m_uiSeq;
CString m_strPrefix;
private:
int m_iWorkers;
int m_iMaxEvents;
FD m_evExit;
IIOHandler* m_pHandler;
unique_ptr<TDispContext[]> m_pContexts;
};

62
common/PollHelper.cpp Normal file
View File

@@ -0,0 +1,62 @@
/*
* Copyright: JessMA Open Source (ldcsaa@gmail.com)
*
* Author : Bruce Liang
* Website : https://github.com/ldcsaa
* Project : https://github.com/ldcsaa/HP-Socket
* Blog : http://www.cnblogs.com/ldcsaa
* Wiki : http://www.oschina.net/p/hp-socket
* QQ Group : 44636872, 75375912
*
* Licensed 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 "PollHelper.h"
#include "FuncHelper.h"
long PollForSingleObject(pollfd& pfd, long lTimeout, const sigset_t* pSigSet)
{
return PollForMultipleObjects(&pfd, 1, lTimeout, pSigSet);
}
long PollForMultipleObjects(pollfd pfds[], int iCount, long lTimeout, const sigset_t* pSigSet)
{
ASSERT(iCount > 0 && iCount < (int)(sizeof(LONG) * 8));
timespec* pts = nullptr;
if(!IS_INFINITE(lTimeout))
{
pts = CreateLocalObject(timespec);
::MillisecondToTimespec(lTimeout, *pts);
}
while(TRUE)
{
int rs = NO_EINTR_INT(ppoll(pfds, iCount, pts, pSigSet));
if(rs <= TIMEOUT) return rs;
LONG lValue = 0L;
for(int i = 0; i < iCount; i++)
{
pollfd& pfd = pfds[i];
if(pfd.revents & _POLL_ALL_EVENTS)
lValue |= (1 << i);
}
return lValue;
}
}

51
common/PollHelper.h Normal file
View File

@@ -0,0 +1,51 @@
/*
* Copyright: JessMA Open Source (ldcsaa@gmail.com)
*
* Author : Bruce Liang
* Website : https://github.com/ldcsaa
* Project : https://github.com/ldcsaa/HP-Socket
* Blog : http://www.cnblogs.com/ldcsaa
* Wiki : http://www.oschina.net/p/hp-socket
* QQ Group : 44636872, 75375912
*
* Licensed 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.
*/
#pragma once
#include "hpsocket/GlobalDef.h"
#include <poll.h>
#include <signal.h>
#if __USE_GNU
#define _POLL_READ_PRI_EVENTS (POLLPRI | POLLRDHUP)
#define _POLL_READ_EVENTS (POLLIN | POLLRDHUP)
#define _POLL_HUNGUP_EVENTS (POLLHUP | POLLRDHUP)
#else
#define _POLL_READ_PRI_EVENTS (POLLPRI)
#define _POLL_READ_EVENTS (POLLIN)
#define _POLL_HUNGUP_EVENTS (POLLHUP)
#endif
#define _POLL_ALL_READ_EVENTS (_POLL_READ_EVENTS | _POLL_READ_PRI_EVENTS)
#define _POLL_WRITE_EVENTS (POLLOUT)
#define _POLL_NORMAL_RW_EVENTS (_POLL_READ_EVENTS | _POLL_WRITE_EVENTS)
#define _POLL_ALL_RW_EVENTS (_POLL_ALL_READ_EVENTS | _POLL_WRITE_EVENTS)
#define _POLL_ERROR_EVENTS (POLLERR | POLLNVAL)
#define _POLL_ALL_ERROR_EVENTS (_POLL_ERROR_EVENTS | _POLL_HUNGUP_EVENTS)
#define _POLL_ALL_NORMAL_EVENTS (_POLL_NORMAL_RW_EVENTS | _POLL_ALL_ERROR_EVENTS)
#define _POLL_ALL_EVENTS (_POLL_ALL_RW_EVENTS | _POLL_ALL_ERROR_EVENTS)
long PollForSingleObject(pollfd& pfd, long lTimeout = INFINITE, const sigset_t* pSigSet = nullptr);
long PollForMultipleObjects(pollfd pfds[], int iCount, long lTimeout = INFINITE, const sigset_t* pSigSet = nullptr);

152
common/PrivateHeap.h Normal file
View File

@@ -0,0 +1,152 @@
/*
* Copyright: JessMA Open Source (ldcsaa@gmail.com)
*
* Author : Bruce Liang
* Website : https://github.com/ldcsaa
* Project : https://github.com/ldcsaa/HP-Socket
* Blog : http://www.cnblogs.com/ldcsaa
* Wiki : http://www.oschina.net/p/hp-socket
* QQ Group : 44636872, 75375912
*
* Licensed 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.
*/
#pragma once
#include "hpsocket/GlobalDef.h"
#include "Singleton.h"
#include <malloc.h>
#define HEAP_ZERO_MEMORY 0x08
class CGlobalHeapImpl
{
public:
PVOID Alloc(SIZE_T dwSize, DWORD dwFlags = 0)
{
PVOID pv = malloc(dwSize);
if(!pv)
throw std::bad_alloc();
if(dwFlags & HEAP_ZERO_MEMORY)
ZeroMemory(pv, dwSize);
return pv;
}
PVOID ReAlloc(PVOID pvMemory, SIZE_T dwSize, DWORD dwFlags = 0)
{
PVOID pv = realloc(pvMemory, dwSize);
if(!pv)
{
if(pvMemory)
free(pvMemory);
throw std::bad_alloc();
}
if(dwFlags & HEAP_ZERO_MEMORY)
ZeroMemory(pv, dwSize);
return pv;
}
BOOL Free(PVOID pvMemory, DWORD dwFlags = 0)
{
if(pvMemory)
{
free(pvMemory);
return TRUE;
}
return FALSE;
}
SIZE_T Compact (DWORD dwFlags = 0) {return -1;}
SIZE_T Size (PVOID pvMemory, DWORD dwFlags = 0) {return _msize(pvMemory);}
BOOL IsValid() {return TRUE;}
BOOL Reset() {return TRUE;}
public:
CGlobalHeapImpl (DWORD dwOptions = 0, SIZE_T dwInitSize = 0, SIZE_T dwMaxSize = 0) {}
~CGlobalHeapImpl() {}
DECLARE_NO_COPY_CLASS(CGlobalHeapImpl)
};
#if !defined (_USE_CUSTOM_PRIVATE_HEAP)
using CPrivateHeap = CGlobalHeapImpl;
#endif
template<class T> class CPrivateHeapBuffer
{
public:
CPrivateHeapBuffer(CPrivateHeap& hpPrivate, SIZE_T dwSize = 0)
: m_hpPrivate (hpPrivate)
, m_pvMemory (nullptr)
{
ASSERT(m_hpPrivate.IsValid());
Alloc(dwSize);
}
~CPrivateHeapBuffer() {Free();}
public:
T* Alloc(SIZE_T dwSize, DWORD dwFlags = 0)
{
if(IsValid())
Free();
if(dwSize > 0)
m_pvMemory = (T*)m_hpPrivate.Alloc(dwSize * sizeof(T), dwFlags);
return m_pvMemory;
}
T* ReAlloc(SIZE_T dwSize, DWORD dwFlags = 0)
{return m_pvMemory = (T*)m_hpPrivate.ReAlloc(m_pvMemory, dwSize * sizeof(T), dwFlags);}
SIZE_T Size(DWORD dwFlags = 0)
{return m_hpPrivate.Size(m_pvMemory, dwFlags) / sizeof(T);}
BOOL Free(DWORD dwFlags = 0)
{
BOOL isOK = TRUE;
if(IsValid())
{
isOK = m_hpPrivate.Free(m_pvMemory, dwFlags);
m_pvMemory = nullptr;
}
return isOK;
}
BOOL IsValid() {return m_pvMemory != nullptr;}
operator T* () const {return m_pvMemory;}
T& operator [] (int i) const {return *(m_pvMemory + i);}
private:
CPrivateHeap& m_hpPrivate;
T* m_pvMemory;
DECLARE_NO_COPY_CLASS(CPrivateHeapBuffer)
};
using CPrivateHeapByteBuffer = CPrivateHeapBuffer<BYTE>;
using CPrivateHeapStrBuffer = CPrivateHeapBuffer<TCHAR>;

228
common/RWLock.cpp Normal file
View File

@@ -0,0 +1,228 @@
/*
* Copyright: JessMA Open Source (ldcsaa@gmail.com)
*
* Author : Bruce Liang
* Website : https://github.com/ldcsaa
* Project : https://github.com/ldcsaa/HP-Socket
* Blog : http://www.cnblogs.com/ldcsaa
* Wiki : http://www.oschina.net/p/hp-socket
* QQ Group : 44636872, 75375912
*
* Licensed 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 "RWLock.h"
CMutexRWLock::CMutexRWLock()
: m_nActive (0)
, m_dwWriterTID (0)
{
}
CMutexRWLock::~CMutexRWLock()
{
ASSERT(m_nActive == 0);
ASSERT(m_dwWriterTID == 0);
}
VOID CMutexRWLock::WaitToRead()
{
BOOL bWait = FALSE;
{
CSpinLock locallock(m_cs);
if(m_nActive > 0)
++m_nActive;
else if(m_nActive == 0)
{
if(m_mtx.try_lock_shared())
++m_nActive;
else
bWait = TRUE;
}
else if(!IsOwner())
bWait = TRUE;
}
if(bWait)
{
m_mtx.lock_shared();
CSpinLock locallock(m_cs);
++m_nActive;
}
}
VOID CMutexRWLock::WaitToWrite()
{
BOOL bWait = FALSE;
{
CSpinLock locallock(m_cs);
if(m_nActive > 0)
bWait = TRUE;
else if(m_nActive == 0)
{
if(m_mtx.try_lock())
{
SetOwner();
--m_nActive;
}
else
bWait = TRUE;
}
else
{
if(IsOwner())
--m_nActive;
else
bWait = TRUE;
}
}
if(bWait)
{
m_mtx.lock();
SetOwner();
--m_nActive;
}
}
VOID CMutexRWLock::ReadDone()
{
ASSERT(m_nActive != 0);
if(m_nActive > 0)
{
{
CSpinLock locallock(m_cs);
--m_nActive;
}
m_mtx.unlock_shared();
}
else
ASSERT(IsOwner());
}
VOID CMutexRWLock::WriteDone()
{
ASSERT(IsOwner());
ASSERT(m_nActive < 0);
BOOL bDone;
{
CSpinLock locallock(m_cs);
bDone = (++m_nActive == 0);
}
if(bDone)
{
DetachOwner();
m_mtx.unlock();
}
else
ASSERT(IsOwner());
}
CSEMRWLock::CSEMRWLock()
: m_nWaitingReaders (0)
, m_nWaitingWriters (0)
, m_nActive (0)
, m_dwWriterTID (0)
{
}
CSEMRWLock::~CSEMRWLock()
{
ASSERT(m_nActive == 0);
ASSERT(m_dwWriterTID == 0);
}
VOID CSEMRWLock::WaitToRead()
{
CMutexLock2 lock(m_mtx);
if(IsOwner())
return;
++m_nWaitingReaders;
m_cvRead.wait(lock, [=]() -> BOOL
{
return m_nActive >= 0 && m_nWaitingWriters == 0;
});
--m_nWaitingReaders;
++m_nActive;
}
VOID CSEMRWLock::WaitToWrite()
{
CMutexLock2 lock(m_mtx);
if(IsOwner())
{
--m_nActive;
return;
}
++m_nWaitingWriters;
m_cvWrite.wait(lock, [=]() -> BOOL
{
return m_nActive == 0;
});
--m_nWaitingWriters;
--m_nActive;
SetOwner();
}
VOID CSEMRWLock::ReadDone()
{
CMutexLock2 locallock(m_mtx);
if(IsOwner())
return;
ASSERT(m_nActive > 0);
if(--m_nActive == 0 && m_nWaitingWriters > 0)
m_cvWrite.notify_one();
}
VOID CSEMRWLock::WriteDone()
{
ASSERT(IsOwner());
CMutexLock2 lock(m_mtx);
if(++m_nActive == 0)
{
DetachOwner();
if(m_nWaitingWriters > 0)
m_cvWrite.notify_one();
else if(m_nWaitingReaders > 0)
m_cvRead.notify_all();
}
}

128
common/RWLock.h Normal file
View File

@@ -0,0 +1,128 @@
/*
* Copyright: JessMA Open Source (ldcsaa@gmail.com)
*
* Author : Bruce Liang
* Website : https://github.com/ldcsaa
* Project : https://github.com/ldcsaa/HP-Socket
* Blog : http://www.cnblogs.com/ldcsaa
* Wiki : http://www.oschina.net/p/hp-socket
* QQ Group : 44636872, 75375912
*
* Licensed 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.
*/
#pragma once
#include "hpsocket/GlobalDef.h"
#include "CriSec.h"
#include <shared_mutex>
#include <condition_variable>
using namespace std;
class CMutexRWLock
{
public:
VOID WaitToRead();
VOID WaitToWrite();
VOID ReadDone();
VOID WriteDone();
private:
BOOL IsOwner() {return ::IsSelfThread(m_dwWriterTID);}
VOID SetOwner() {m_dwWriterTID = SELF_THREAD_ID;}
VOID DetachOwner() {m_dwWriterTID = 0;}
public:
CMutexRWLock();
~CMutexRWLock();
DECLARE_NO_COPY_CLASS(CMutexRWLock)
private:
int m_nActive;
THR_ID m_dwWriterTID;
CSpinGuard m_cs;
shared_timed_mutex m_mtx;
};
class CSEMRWLock
{
public:
VOID WaitToRead();
VOID WaitToWrite();
VOID ReadDone();
VOID WriteDone();
private:
BOOL IsOwner() {BOOL bOwner = ::IsSelfThread(m_dwWriterTID); ASSERT(!bOwner || m_nActive < 0); return bOwner;}
VOID SetOwner() {m_dwWriterTID = SELF_THREAD_ID;}
VOID DetachOwner() {m_dwWriterTID = 0;}
public:
CSEMRWLock();
~CSEMRWLock();
DECLARE_NO_COPY_CLASS(CSEMRWLock)
private:
int m_nWaitingReaders;
int m_nWaitingWriters;
int m_nActive;
THR_ID m_dwWriterTID;
CMTX m_mtx;
condition_variable m_cvRead;
condition_variable m_cvWrite;
};
template<class CLockObj> class CLocalReadLock
{
public:
CLocalReadLock(CLockObj& obj) : m_wait(obj) {m_wait.WaitToRead();}
~CLocalReadLock() {m_wait.ReadDone();}
DECLARE_NO_COPY_CLASS(CLocalReadLock)
private:
CLockObj& m_wait;
};
template<class CLockObj> class CLocalWriteLock
{
public:
CLocalWriteLock(CLockObj& obj) : m_wait(obj) {m_wait.WaitToWrite();}
~CLocalWriteLock() {m_wait.WriteDone();}
DECLARE_NO_COPY_CLASS(CLocalWriteLock)
private:
CLockObj& m_wait;
};
using CSimpleRWLock = shared_timed_mutex;
using CReadLock = shared_lock<shared_timed_mutex>;
using CWriteLock = lock_guard<shared_timed_mutex>;
using CWriteLock2 = unique_lock<shared_timed_mutex>;
#if !defined(_USE_MUTEX_RW_LOCK)
using CRWLock = CSEMRWLock;
#else
using CRWLock = CMutexRWLock;
#endif
using CReentrantReadLock = CLocalReadLock<CRWLock>;
using CReentrantWriteLock = CLocalWriteLock<CRWLock>;

1731
common/RingBuffer.h Normal file

File diff suppressed because it is too large Load Diff

1068
common/STLHelper.h Normal file

File diff suppressed because it is too large Load Diff

117
common/Semaphore.h Normal file
View File

@@ -0,0 +1,117 @@
/*
* Copyright: JessMA Open Source (ldcsaa@gmail.com)
*
* Author : Bruce Liang
* Website : https://github.com/ldcsaa
* Project : https://github.com/ldcsaa/HP-Socket
* Blog : http://www.cnblogs.com/ldcsaa
* Wiki : http://www.oschina.net/p/hp-socket
* QQ Group : 44636872, 75375912
*
* Licensed 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.
*/
#pragma once
#include "hpsocket/GlobalDef.h"
#include "CriSec.h"
#include <condition_variable>
using namespace std;
class CSEM
{
public:
void Wait()
{
CMutexLock2 lock(m_mtx);
m_cv.wait(lock);
}
template<typename _Predicate>
void Wait(_Predicate p)
{
CMutexLock2 lock(m_mtx);
m_cv.wait(lock, p);
}
template<typename _Rep, typename _Period>
cv_status WaitFor(const chrono::duration<_Rep, _Period>& t)
{
CMutexLock2 lock(m_mtx);
return m_cv.wait_for(lock, t);
}
cv_status WaitFor(DWORD dwMilliseconds)
{
return WaitFor(chrono::milliseconds(dwMilliseconds));
}
template<typename _Rep, typename _Period, typename _Predicate>
bool WaitFor(const chrono::duration<_Rep, _Period>& t, _Predicate p)
{
CMutexLock2 lock(m_mtx);
return m_cv.wait_for(lock, t, p);
}
template<typename _Predicate>
bool WaitFor(DWORD dwMilliseconds, _Predicate p)
{
if(IS_INFINITE(dwMilliseconds))
{
Wait(p);
return true;
}
return WaitFor(chrono::milliseconds(dwMilliseconds), p);
}
void NotifyOne()
{
m_cv.notify_one();
}
void NotifyAll()
{
m_cv.notify_all();
}
void SyncNotifyOne()
{
CMutexLock2 lock(m_mtx);
NotifyOne();
}
void SyncNotifyAll()
{
CMutexLock2 lock(m_mtx);
NotifyAll();
}
private:
CMTX m_mtx;
condition_variable m_cv;
DECLARE_NO_COPY_CLASS(CSEM)
DECLARE_PUBLIC_DEFAULT_CONSTRUCTOR(CSEM)
};
using CCVLock = CSEM;

183
common/SignalHandler.h Normal file
View File

@@ -0,0 +1,183 @@
/*
* Copyright: JessMA Open Source (ldcsaa@gmail.com)
*
* Author : Bruce Liang
* Website : https://github.com/ldcsaa
* Project : https://github.com/ldcsaa/HP-Socket
* Blog : http://www.cnblogs.com/ldcsaa
* Wiki : http://www.oschina.net/p/hp-socket
* QQ Group : 44636872, 75375912
*
* Licensed 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.
*/
#pragma once
#include "hpsocket/GlobalDef.h"
#include "Thread.h"
#include <signal.h>
#include <memory>
using namespace std;
template<class T> class CSignalHandler
{
public:
using MT = CSignalHandler<T>;
using SI = siginfo_t;
using SS = sigset_t;
using CHandlerThread = CThread<MT, const SS, VOID>;
using F = VOID (T::*)(const SI*);
using SF = VOID (*)(const SI*);
using SSPTR = unique_ptr<SS>;
friend CHandlerThread;
public:
BOOL Setup(SF pFunc, const SS* pSigSet, BOOL bRestorOnCancel = TRUE)
{
return Setup((__CFakeRunnerClass_*)nullptr, *(F*)&pFunc, pSigSet, bRestorOnCancel);
}
BOOL Setup(T* pRunner, F pFunc, const SS* pSigSet, BOOL bRestorOnCancel = TRUE)
{
ASSERT_CHECK_EINVAL(pSigSet != nullptr);
m_pssPre = make_unique<SS>();
int rs = pthread_sigmask(SIG_BLOCK, pSigSet, m_pssPre.get());
if(rs != NO_ERROR)
{
m_pssPre = nullptr;
::SetLastError(rs);
return FALSE;
}
m_pRunner = pRunner;
m_pFunc = pFunc;
m_pssCur = make_unique<SS>();
::CopyPlainObject(m_pssCur.get(), pSigSet);
BOOL isOK = m_thHandler.Start(this, &MT::ThreadFunc, m_pssCur.get());
if(isOK && !bRestorOnCancel)
m_pssPre = nullptr;
else if(!isOK)
EXECUTE_RESTORE_ERROR(Reset());
return isOK;
}
BOOL Cancel()
{
BOOL isOK = m_thHandler.IsRunning();
if(isOK)
{
isOK = m_thHandler.Interrupt();
isOK &= m_thHandler.Join();
}
isOK &= Reset();
return isOK;
}
BOOL IsRunning () {return m_thHandler.IsRunning();}
T* GetRunner () {return m_pRunner;}
F GetFunc () {return m_pFunc;}
SF GetSFunc () {return *(SF*)&m_pFunc;}
THR_ID GetThreadID () {return m_thHandler.GetThreadID();}
NTHR_ID GetNativeID () {return m_thHandler.GetNativeID();}
private:
VOID ThreadFunc(const SS* pSigSet)
{
ASSERT(pSigSet == m_pssCur.get());
SI si;
ZeroObject(si);
while(!::IsThreadInterrupted())
{
#if !defined(__ANDROID__)
int rs = NO_EINTR_EXCEPT_THR_INTR_INT(sigwaitinfo(pSigSet, &si));
#else
int rs = NO_EINTR_EXCEPT_THR_INTR_INT(sigwait(pSigSet, &si.si_signo));
#endif
if(IS_HAS_ERROR(rs))
{
if(IS_ERROR(EINTR))
{
ASSERT(::IsThreadInterrupted());
break;
}
ERROR_ABORT();
}
Run((T*)nullptr, &si);
}
}
template<typename T_, typename = enable_if_t<!is_same<T_, __CFakeRunnerClass_>::value>>
VOID Run(T_*, const SI* pSigSet) {(m_pRunner->*m_pFunc)(pSigSet);}
VOID Run(__CFakeRunnerClass_*, const SI* pSigSet) {(*(SF*)&m_pFunc)(pSigSet);}
BOOL Reset()
{
BOOL isOK = TRUE;
if(m_pssPre)
{
isOK = (pthread_sigmask(SIG_SETMASK, m_pssPre.get(), nullptr) == NO_ERROR);
m_pssPre = nullptr;
}
m_pssCur = nullptr;
m_pRunner = nullptr;
m_pFunc = nullptr;
return isOK;
}
public:
CSignalHandler()
{
}
virtual ~CSignalHandler()
{
Cancel();
}
DECLARE_NO_COPY_CLASS(CSignalHandler)
private:
T* m_pRunner;
F m_pFunc;
SSPTR m_pssCur;
SSPTR m_pssPre;
CHandlerThread m_thHandler;
};
using CStaticSignalHandler = CSignalHandler<__CFakeRunnerClass_>;

117
common/Singleton.h Normal file
View File

@@ -0,0 +1,117 @@
/*
* Copyright: JessMA Open Source (ldcsaa@gmail.com)
*
* Author : Bruce Liang
* Website : https://github.com/ldcsaa
* Project : https://github.com/ldcsaa/HP-Socket
* Blog : http://www.cnblogs.com/ldcsaa
* Wiki : http://www.oschina.net/p/hp-socket
* QQ Group : 44636872, 75375912
*
* Licensed 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.
*/
#pragma once
#include "hpsocket/GlobalDef.h"
#define SINGLETON_THIS(ClassName) ClassName::GetThis()
#define SINGLETON_INSTANCE(ClassName) ClassName::GetInstance()
#define SINGLETON_OBJECT(ObjName) SINGLETON_INSTANCE(C##ObjName)
#define DEFINE_SINGLETON(ClassName) \
ClassName* ClassName::m_pThis = nullptr;
#define DEFINE_P_THIS(ClassName) \
DEFINE_SINGLETON(ClassName)
#define DECLARE_SINGLETON_INTERFACE(ClassName) \
public: \
static ClassName* GetThis() {return m_pThis;} \
static ClassName& GetInstance() {return *m_pThis;} \
protected: \
static ClassName* m_pThis;
#define DECLARE_SINGLETON_CREATE_INSTANCE(ClassName) \
public: \
static BOOL CreateInstance() \
{ \
if(!m_pThis) \
m_pThis = new ClassName; \
\
return m_pThis != nullptr; \
} \
\
static BOOL DeleteInstance() \
{ \
if(m_pThis) \
{ \
delete m_pThis; \
m_pThis = nullptr; \
} \
\
return m_pThis == nullptr; \
}
#define DECLARE_PUBLIC_DEFAULT_CONSTRUCTOR(ClassName) \
public: \
ClassName() = default;
#define DECLARE_PRIVATE_DEFAULT_CONSTRUCTOR(ClassName) \
private: \
ClassName() = default;
#define DECLARE_PRIVATE_COPY_CONSTRUCTOR(ClassName) \
private: \
ClassName(const ClassName&); \
ClassName& operator = (const ClassName&);
#define DECLARE_NO_COPY_CLASS(ClassName) \
private: \
ClassName(const ClassName&) = delete; \
ClassName& operator = (const ClassName&) = delete;
#define DECLARE_SINGLETON_IMPLEMENT_NO_CREATE_INSTANCE(ClassName) \
DECLARE_SINGLETON_INTERFACE(ClassName) \
DECLARE_PRIVATE_DEFAULT_CONSTRUCTOR(ClassName) \
DECLARE_NO_COPY_CLASS(ClassName)
#define DECLARE_SINGLETON_IMPLEMENT_NO_DEFAULT_CONSTRUCTOR(ClassName) \
DECLARE_SINGLETON_CREATE_INSTANCE(ClassName) \
DECLARE_NO_COPY_CLASS(ClassName)
#define DECLARE_SINGLETON_IMPLEMENT(ClassName) \
DECLARE_SINGLETON_IMPLEMENT_NO_DEFAULT_CONSTRUCTOR(ClassName) \
DECLARE_PRIVATE_DEFAULT_CONSTRUCTOR(ClassName)
#define DECLARE_SINGLETON_NO_DEFAULT_CONSTRUCTOR(ClassName) \
DECLARE_SINGLETON_INTERFACE(ClassName) \
DECLARE_SINGLETON_IMPLEMENT_NO_DEFAULT_CONSTRUCTOR(ClassName)
#define DECLARE_SINGLETON(ClassName) \
DECLARE_SINGLETON_NO_DEFAULT_CONSTRUCTOR(ClassName) \
DECLARE_PRIVATE_DEFAULT_CONSTRUCTOR(ClassName)
template<class T> class CSingleObject
{
public:
CSingleObject () {T::CreateInstance();}
~CSingleObject () {T::DeleteInstance();}
T* GetPointer () {return T::GetThis();}
T& GetObject () {return T::GetInstance();}
BOOL IsValid () {return GetPointer() != nullptr;}
};
#define DECLARE_SINGLE_OBJECT(ClassName) CSingleObject<ClassName> _##ClassName##_Single_Object_;

930
common/StringT.h Normal file
View File

@@ -0,0 +1,930 @@
/*
* Copyright: JessMA Open Source (ldcsaa@gmail.com)
*
* Author : Bruce Liang
* Website : https://github.com/ldcsaa
* Project : https://github.com/ldcsaa/HP-Socket
* Blog : http://www.cnblogs.com/ldcsaa
* Wiki : http://www.oschina.net/p/hp-socket
* QQ Group : 44636872, 75375912
*
* Licensed 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.
*/
#pragma once
#include "FuncHelper.h"
#include <stdarg.h>
#include <string.h>
#include <string>
using namespace std;
template<typename _CharT, typename _Traits = char_traits<_CharT>, typename _Alloc = allocator<_CharT>>
class CStringT : public basic_string<_CharT, _Traits, _Alloc>
{
public:
using __super = basic_string<_CharT, _Traits, _Alloc>;
using XCHAR = _CharT;
using PXSTR = _CharT*;
using PCXSTR = const _CharT*;
using traits_type = typename __super::traits_type;
using value_type = typename __super::value_type;
using allocator_type = typename __super::allocator_type;
using size_type = typename __super::size_type;
using difference_type = typename __super::difference_type;
using reference = typename __super::reference;
using const_reference = typename __super::const_reference;
using pointer = typename __super::pointer;
using const_pointer = typename __super::const_pointer;
using iterator = typename __super::iterator;
using const_iterator = typename __super::const_iterator;
using const_reverse_iterator = typename __super::const_reverse_iterator;
using reverse_iterator = typename __super::reverse_iterator;
using __super::clear;
using __super::empty;
using __super::size;
using __super::resize;
using __super::data;
using __super::c_str;
private:
constexpr static PCXSTR SPACE_CHARS = _T(" \t\r\n\f\v");
public:
void Empty() {clear();}
bool IsEmpty() const {return empty();}
int GetLength() const {return (int)size();}
const _CharT* GetString() const {return c_str();}
operator const _CharT* () const {return __super::c_str();}
_CharT* GetBuffer(int length)
{
resize((size_type)length);
return (_CharT*)data();
}
void ReleaseBuffer(int length = -1)
{
if(length == -1)
length = lstrlen(data());
resize(length);
}
void ReleaseBufferSetLength(int length)
{
ASSERT(length >=0);
ReleaseBuffer(length);
}
void Truncate(int length)
{
if(length >= GetLength())
return;
ReleaseBuffer(length);
}
int Format(const _CharT* format, ...)
{
int rs;
va_list ap;
va_start(ap, format);
rs = VASprintf(0, format, ap);
va_end(ap);
return rs;
}
int AppendFormat(const _CharT* format, ...)
{
int rs;
va_list ap;
va_start(ap, format);
rs = VASprintf(GetLength(), format, ap);
va_end(ap);
return rs;
}
int VASprintf(int offset, const _CharT* format, va_list ap)
{
va_list ap_cpy;
va_copy(ap_cpy, ap);
int count = vsnprintf(nullptr, 0, format, ap);
if(count >= 0)
{
_CharT* p = GetBuffer(count + offset);
vsnprintf(p + offset, count + 1, format, ap_cpy);
}
va_end(ap_cpy);
return count;
}
CStringT& Append(const _CharT* __s)
{
append(__s);
return *this;
}
CStringT& Append(const _CharT* __s, int __n)
{
append(__s, __n);
return *this;
}
CStringT& AppendChar(_CharT __c)
{
push_back(__c);
return *this;
}
int Compare(const _CharT* __s) const
{
return lstrcmp(c_str(), __s);
}
int CompareNoCase(const _CharT* __s) const
{
return lstricmp(c_str(), __s);
}
bool Equals(const _CharT* __s) const
{
return (Compare(__s) == 0);
}
bool EqualsNoCase(const _CharT* __s) const
{
return (CompareNoCase(__s) == 0);
}
CStringT& MakeLower()
{
size_type s = size();
_CharT* p = (_CharT*)c_str();
_CharT c;
for(size_type i = 0; i < s; i++)
{
c = p[i];
if(c >= 'A' && c <= 'Z')
p[i] = (_CharT)(c + 32);
}
return *this;
}
CStringT& MakeUpper()
{
size_type s = size();
_CharT* p = (_CharT*)c_str();
_CharT c;
for(size_type i = 0; i < s; i++)
{
c = p[i];
if(c >= 'a' && c <= 'z')
p[i] = (_CharT)(c - 32);
}
return *this;
}
CStringT Mid(int iFirst, int nCount = (int)__super::npos) const
{
return substr(iFirst, nCount);
}
CStringT Left(int nCount) const
{
return Mid(0, nCount);
}
CStringT Right(int nCount) const
{
int nLength = GetLength();
if(nCount >= nLength)
return *this;
return Mid(nLength - nCount, nCount);
}
CStringT Tokenize(PCXSTR lpszTokens, int& iStart) const
{
ASSERT(iStart >= 0);
if((lpszTokens == nullptr) || (*lpszTokens == (_CharT)0))
{
if(iStart < GetLength())
return CStringT(GetString() + iStart);
}
else
{
PCXSTR pszPlace = GetString() + iStart;
PCXSTR pszEnd = GetString() + GetLength();
if(pszPlace < pszEnd)
{
int nIncluding = lstrspn(pszPlace, lpszTokens);
if((pszPlace + nIncluding) < pszEnd)
{
pszPlace += nIncluding;
int nExcluding = lstrcspn(pszPlace, lpszTokens);
int iFrom = iStart + nIncluding;
int nUntil = nExcluding;
iStart = iFrom + nUntil + 1;
return Mid(iFrom, nUntil);
}
}
}
iStart = -1;
return CStringT();
}
CStringT& Trim()
{
return Trim(SPACE_CHARS);
}
CStringT& TrimRight()
{
return TrimRight(SPACE_CHARS);
}
CStringT& TrimLeft()
{
return TrimLeft(SPACE_CHARS);
}
CStringT& Trim(XCHAR c)
{
return(TrimRight(c).TrimLeft(c));
}
CStringT& TrimRight(XCHAR c)
{
int iLength = GetLength();
if(iLength == 0)
return *this;
PCXSTR lpszBegin = GetString();
PCXSTR lpszEnd = lpszBegin + iLength;
while(lpszEnd > lpszBegin)
{
if(*(lpszEnd - 1) != c)
break;
--lpszEnd;
}
int iNewLength = (int)(lpszEnd - lpszBegin);
if(iNewLength < iLength)
Truncate(iNewLength);
return *this;
}
CStringT& TrimLeft(XCHAR c)
{
int iLength = GetLength();
if(iLength == 0)
return *this;
PCXSTR lpszBegin = GetString();
PCXSTR lpszEnd = lpszBegin;
int iOffset = 0;
while(*lpszEnd == c)
{
++lpszEnd;
++iOffset;
if(iOffset == iLength)
break;
}
if(iOffset != 0)
{
int iNewLength = iLength - iOffset;
if(iNewLength > 0)
memcpy((PXSTR)lpszBegin, lpszEnd, (iLength - iOffset) * sizeof(XCHAR));
ReleaseBufferSetLength(iNewLength);
}
return *this;
}
CStringT& Trim(PCXSTR lpszChars)
{
return(TrimRight(lpszChars).TrimLeft(lpszChars));
}
CStringT& TrimRight(PCXSTR lpszChars)
{
ASSERT(!::IsStrEmpty(lpszChars));
if(::IsStrEmpty(lpszChars))
return *this;
int iLength = GetLength();
if(iLength == 0)
return *this;
PCXSTR lpszBegin = GetString();
PCXSTR lpszEnd = lpszBegin + iLength;
while(lpszEnd > lpszBegin)
{
if(::StrChr(lpszChars, *(lpszEnd - 1)) == nullptr)
break;
--lpszEnd;
}
int iNewLength = (int)(lpszEnd - lpszBegin);
if(iNewLength < iLength)
Truncate(iNewLength);
return *this;
}
CStringT& TrimLeft(PCXSTR lpszChars)
{
ASSERT(!::IsStrEmpty(lpszChars));
if(::IsStrEmpty(lpszChars))
return *this;
int iLength = GetLength();
if(iLength == 0)
return *this;
PCXSTR lpszBegin = GetString();
PCXSTR lpszEnd = lpszBegin;
int iOffset = 0;
while(::StrChr(lpszChars, *lpszEnd) != nullptr)
{
++lpszEnd;
++iOffset;
if(iOffset == iLength)
break;
}
if(iOffset != 0)
{
int iNewLength = iLength - iOffset;
if(iNewLength > 0)
memcpy((PXSTR)lpszBegin, lpszEnd, (iLength - iOffset) * sizeof(XCHAR));
ReleaseBufferSetLength(iNewLength);
}
return *this;
}
int Find(XCHAR c, int iStart = 0) const
{
ASSERT(iStart >= 0);
int iLength = GetLength();
if(iStart < 0 || iStart >= iLength)
return -1;
PCXSTR lpszBegin = GetString();
PCXSTR lpszFind = ::StrChr(lpszBegin + iStart, c);
return ((lpszFind == nullptr) ? -1 : (int)(lpszFind - lpszBegin));
}
int Find(PCXSTR lpszSub, int iStart = 0) const
{
ASSERT(iStart >= 0 && !::IsStrEmpty(lpszSub));
int iLength = GetLength();
if(lpszSub == nullptr || iStart < 0 || iStart > iLength)
return -1;
PCXSTR lpszBegin = GetString();
PCXSTR lpszFind = ::StrStr(lpszBegin + iStart, lpszSub);
return ((lpszFind == nullptr) ? -1 : (int)(lpszFind - lpszBegin));
}
int FindOneOf(PCXSTR lpszChars) const
{
ASSERT(!::IsStrEmpty(lpszChars));
if(lpszChars == nullptr)
return -1;
PCXSTR lpszBegin = GetString();
PCXSTR lpszFind = ::StrPBrk(lpszBegin, lpszChars);
return ((lpszFind == nullptr) ? -1 : (int)(lpszFind - lpszBegin));
}
int ReverseFind(XCHAR c) const
{
PCXSTR lpszBegin = GetString();
PCXSTR lpszFind = ::StrRChr(lpszBegin, c);
return ((lpszFind == nullptr) ? -1 : (int)(lpszFind - lpszBegin));
}
int Remove(XCHAR c)
{
int iLength = GetLength();
if(iLength == 0)
return 0;
PCXSTR lpszBegin = GetString();
PXSTR lpszCur = (PXSTR)lpszBegin;
PCXSTR lpszEnd = lpszBegin + iLength;
int iRemoved = 0;
while(lpszCur < lpszEnd)
{
if(*lpszCur == c)
++iRemoved;
else if(iRemoved > 0)
*(lpszCur - iRemoved) = *lpszCur;
++lpszCur;
}
if(iRemoved > 0)
ReleaseBufferSetLength(iLength - iRemoved);
return iRemoved;
}
XCHAR GetAt(int i) const
{
return (*this)[i];
}
void SetAt(int i, XCHAR c)
{
(*this)[i] = c;
}
XCHAR operator[](int i) const
{
ASSERT(i >= 0 && i < GetLength());
return *(GetString() + i);
}
XCHAR& operator[](int i)
{
ASSERT(i >= 0 && i < GetLength());
return *(PXSTR)(GetString() + i);
}
CStringT& Insert(int i, XCHAR c)
{
return insert((size_type)i, 1, c);
}
CStringT& Insert(int i, PCXSTR lpszChars)
{
return insert((size_type)i, lpszChars);
}
CStringT& SetString(PCXSTR lpszStr)
{
return assign(lpszStr);
}
CStringT& SetString(PCXSTR lpszStr, int iLength)
{
return assign(lpszStr, iLength);
}
friend bool operator==(const CStringT& str1, const CStringT& str2)
{
return (str1.Compare(str2) == 0);
}
friend bool operator==(const CStringT& str1, const _CharT* psz2)
{
return (str1.Compare(psz2) == 0);
}
friend bool operator==(const _CharT* psz1, const CStringT& str2)
{
return (str2.Compare(psz1) == 0);
}
friend bool operator!=(const CStringT& str1, const CStringT& str2)
{
return !(str1 == str2);
}
friend bool operator!=(const CStringT& str1, const _CharT* psz2)
{
return !(str1 == psz2);
}
friend bool operator!=(const _CharT* psz1, const CStringT& str2)
{
return !(psz1 == str2);
}
public:
CStringT() : __super() {};
explicit CStringT(const _Alloc& __a)
: __super(__a) {}
CStringT(const __super& __str)
: __super(__str) {}
CStringT(const CStringT& __str)
: __super(__str) {}
CStringT(const __super& __str, size_type __pos, size_type __n = __super::npos)
: __super(__str, __pos, __n) {}
CStringT(const __super& __str, size_type __pos, size_type __n, const _Alloc& __a)
: __super(__str, __pos, __n, __a) {}
CStringT(const _CharT* __s, size_type __n, const _Alloc& __a = _Alloc())
: __super(::SafeStr(__s), __n, __a) {}
CStringT(const _CharT* __s, const _Alloc& __a = _Alloc())
: __super(::SafeStr(__s), __a) {}
CStringT(size_type __n, _CharT __c, const _Alloc& __a = _Alloc())
: __super(__n, __c, __a) {}
#if __cplusplus >= 201103L
CStringT(__super&& __str)
: __super(__str) {}
CStringT(CStringT&& __str)
: __super(__str) {}
CStringT(initializer_list<_CharT> __l, const _Alloc& __a = _Alloc())
: __super(__l, __a) {}
#endif // C++11
template<class _InputIterator>
CStringT(_InputIterator __beg, _InputIterator __end, const _Alloc& __a = _Alloc())
: __super(__beg, __end, __a) {}
~CStringT() = default;
CStringT& operator=(const __super& __str)
{__super::operator=(__str); return *this;}
CStringT& operator=(const CStringT& __str)
{__super::operator=(__str); return *this;}
CStringT& operator=(const _CharT* __s)
{__super::operator=(::SafeStr(__s)); return *this;}
CStringT& operator=(_CharT __c)
{__super::operator=(__c); return *this;}
#if __cplusplus >= 201103L
CStringT& operator=(__super&& __str)
{__super::operator=(__str); return *this;}
CStringT& operator=(CStringT&& __str)
{__super::operator=(__str); return *this;}
CStringT& operator=(initializer_list<_CharT> __l)
{__super::operator=(__l); return *this;}
#endif // C++11
public:
CStringT& operator+=(const __super& __str)
{__super::operator+=(__str); return *this;}
CStringT& operator+=(const _CharT* __s)
{__super::operator+=(::SafeStr(__s)); return *this;}
CStringT& operator+=(_CharT __c)
{__super::operator+=(__c); return *this;}
#if __cplusplus >= 201103L
CStringT& operator+=(initializer_list<_CharT> __l)
{__super::operator+=(__l); return *this;}
#endif // C++11
CStringT& append(const __super& __str)
{__super::append(__str); return *this;}
CStringT& append(const __super& __str, size_type __pos, size_type __n)
{__super::append(__str, __pos, __n); return *this;}
CStringT& append(const _CharT* __s, size_type __n)
{__super::append(::SafeStr(__s), __n); return *this;}
CStringT& append(const _CharT* __s)
{__super::append(::SafeStr(__s)); return *this;}
CStringT& append(size_type __n, _CharT __c)
{__super::append(__n, __c); return *this;}
#if __cplusplus >= 201103L
CStringT& append(initializer_list<_CharT> __l)
{__super::append(__l); return *this;}
#endif // C++11
template<class _InputIterator>
CStringT& append(_InputIterator __first, _InputIterator __last)
{__super::append(__first, __last); return *this;}
void push_back(_CharT __c)
{__super::push_back(__c);}
CStringT& assign(const __super& __str)
{__super::assign(__str); return *this;}
#if __cplusplus >= 201103L
CStringT& assign(__super&& __str)
{__super::assign(__str); return *this;}
#endif // C++11
CStringT& assign(const __super& __str, size_type __pos, size_type __n)
{__super::assign(__str, __pos, __n); return *this;}
CStringT& assign(const _CharT* __s, size_type __n)
{__super::assign(::SafeStr(__s), __n); return *this;}
CStringT& assign(const _CharT* __s)
{__super::assign(::SafeStr(__s)); return *this;}
CStringT& assign(size_type __n, _CharT __c)
{__super::assign(__n, __c); return *this;}
template<class _InputIterator>
CStringT& assign(_InputIterator __first, _InputIterator __last)
{__super::assign(__first, __last); return *this;}
#if __cplusplus >= 201103L
CStringT& assign(initializer_list<_CharT> __l)
{__super::assign(__l); return *this;}
#endif // C++11
CStringT& insert(size_type __pos1, const __super& __str)
{__super::insert(__pos1, __str); return *this;}
CStringT& insert(size_type __pos1, const __super& __str, size_type __pos2, size_type __n)
{__super::insert(__pos1, __str, __pos2, __n); return *this;}
CStringT& insert(size_type __pos, const _CharT* __s, size_type __n)
{__super::insert(__pos, __s, __n); return *this;}
CStringT& insert(size_type __pos, const _CharT* __s)
{__super::insert(__pos, __s); return *this;}
CStringT& insert(size_type __pos, size_type __n, _CharT __c)
{__super::insert(__pos, __n, __c); return *this;}
CStringT& erase(size_type __pos = 0, size_type __n = __super::npos)
{__super::erase(__pos, __n); return *this;}
CStringT& replace(size_type __pos, size_type __n, const __super& __str)
{__super::replace(__pos, __n, __str); return *this;}
CStringT& replace(size_type __pos1, size_type __n1, const __super& __str, size_type __pos2, size_type __n2)
{__super::replace(__pos1, __n1, __str, __pos2, __n2); return *this;}
CStringT& replace(size_type __pos, size_type __n1, const _CharT* __s, size_type __n2)
{__super::replace(__pos, __n1, __s, __n2); return *this;}
CStringT& replace(size_type __pos, size_type __n1, const _CharT* __s)
{__super::replace(__pos, __n1, __s); return *this;}
CStringT& replace(size_type __pos, size_type __n1, size_type __n2, _CharT __c)
{__super::replace(__pos, __n1, __n2, __c); return *this;}
CStringT& replace(iterator __i1, iterator __i2, const __super& __str)
{__super::replace(__i1, __i2, __str); return *this;}
CStringT& replace(iterator __i1, iterator __i2, const _CharT* __s, size_type __n)
{__super::replace(__i1, __i2, __s, __n); return *this;}
CStringT& replace(iterator __i1, iterator __i2, const _CharT* __s)
{__super::replace(__i1, __i2, __s); return *this;}
CStringT& replace(iterator __i1, iterator __i2, size_type __n, _CharT __c)
{__super::replace(__i1, __i2, __n, __c); return *this;}
template<class _InputIterator>
CStringT& replace(iterator __i1, iterator __i2, _InputIterator __k1, _InputIterator __k2)
{__super::replace(__i1, __i2, __k1, __k2); return *this;}
CStringT& replace(iterator __i1, iterator __i2, _CharT* __k1, _CharT* __k2)
{__super::replace(__i1, __i2, __k1, __k2); return *this;}
CStringT& replace(iterator __i1, iterator __i2, const _CharT* __k1, const _CharT* __k2)
{__super::replace(__i1, __i2, __k1, __k2); return *this;}
CStringT& replace(iterator __i1, iterator __i2, iterator __k1, iterator __k2)
{__super::replace(__i1, __i2, __k1, __k2); return *this;}
CStringT& replace(iterator __i1, iterator __i2, const_iterator __k1, const_iterator __k2)
{__super::replace(__i1, __i2, __k1, __k2); return *this;}
#if __cplusplus >= 201103L
CStringT& replace(iterator __i1, iterator __i2, initializer_list<_CharT> __l)
{__super::replace(__i1, __i2, __l); return *this;}
#endif // C++11
CStringT substr(size_type __pos = 0, size_type __n = __super::npos) const
{return __super::substr(__pos, __n);}
};
template<typename _CharT, typename _Traits, typename _Alloc>
CStringT<_CharT, _Traits, _Alloc>
operator+(const CStringT<_CharT, _Traits, _Alloc>& __lhs, const CStringT<_CharT, _Traits, _Alloc>& __rhs)
{
CStringT<_CharT, _Traits, _Alloc> __str(__lhs);
__str.append(__rhs);
return __str;
}
template<typename _CharT, typename _Traits, typename _Alloc>
CStringT<_CharT,_Traits,_Alloc>
operator+(const _CharT* __lhs, const CStringT<_CharT,_Traits,_Alloc>& __rhs);
template<typename _CharT, typename _Traits, typename _Alloc>
CStringT<_CharT,_Traits,_Alloc>
operator+(_CharT __lhs, const CStringT<_CharT,_Traits,_Alloc>& __rhs);
template<typename _CharT, typename _Traits, typename _Alloc>
inline CStringT<_CharT, _Traits, _Alloc>
operator+(const CStringT<_CharT, _Traits, _Alloc>& __lhs, const _CharT* __rhs)
{
CStringT<_CharT, _Traits, _Alloc> __str(__lhs);
__str.append(__rhs);
return __str;
}
template<typename _CharT, typename _Traits, typename _Alloc>
inline CStringT<_CharT, _Traits, _Alloc>
operator+(const CStringT<_CharT, _Traits, _Alloc>& __lhs, _CharT __rhs)
{
typedef CStringT<_CharT, _Traits, _Alloc> __string_type;
typedef typename __string_type::size_type __size_type;
__string_type __str(__lhs);
__str.append(__size_type(1), __rhs);
return __str;
}
#if __cplusplus >= 201103L
template<typename _CharT, typename _Traits, typename _Alloc>
inline CStringT<_CharT, _Traits, _Alloc>
operator+(CStringT<_CharT, _Traits, _Alloc>&& __lhs, const CStringT<_CharT, _Traits, _Alloc>& __rhs)
{return std::move(__lhs.append(__rhs));}
template<typename _CharT, typename _Traits, typename _Alloc>
inline CStringT<_CharT, _Traits, _Alloc>
operator+(const CStringT<_CharT, _Traits, _Alloc>& __lhs, CStringT<_CharT, _Traits, _Alloc>&& __rhs)
{return std::move(__rhs.insert(0, __lhs));}
template<typename _CharT, typename _Traits, typename _Alloc>
inline CStringT<_CharT, _Traits, _Alloc>
operator+(CStringT<_CharT, _Traits, _Alloc>&& __lhs, CStringT<_CharT, _Traits, _Alloc>&& __rhs)
{
const auto __size = __lhs.size() + __rhs.size();
const bool __cond = (__size > __lhs.capacity()
&& __size <= __rhs.capacity());
return __cond ? std::move(__rhs.insert(0, __lhs))
: std::move(__lhs.append(__rhs));
}
template<typename _CharT, typename _Traits, typename _Alloc>
inline CStringT<_CharT, _Traits, _Alloc>
operator+(const _CharT* __lhs, CStringT<_CharT, _Traits, _Alloc>&& __rhs)
{return std::move(__rhs.insert(0, __lhs));}
template<typename _CharT, typename _Traits, typename _Alloc>
inline CStringT<_CharT, _Traits, _Alloc>
operator+(_CharT __lhs, CStringT<_CharT, _Traits, _Alloc>&& __rhs)
{return std::move(__rhs.insert(0, 1, __lhs));}
template<typename _CharT, typename _Traits, typename _Alloc>
inline CStringT<_CharT, _Traits, _Alloc>
operator+(CStringT<_CharT, _Traits, _Alloc>&& __lhs, const _CharT* __rhs)
{return std::move(__lhs.append(__rhs));}
template<typename _CharT, typename _Traits, typename _Alloc>
inline CStringT<_CharT, _Traits, _Alloc>
operator+(CStringT<_CharT, _Traits, _Alloc>&& __lhs, _CharT __rhs)
{return std::move(__lhs.append(1, __rhs));}
#endif
using CStringA = CStringT<char>;
using CStringW = CStringT<wchar_t>;
using CStdStringA = string;
using CStdStringW = wstring;
#ifdef _UNICODE
using CString = CStringW;
using CStdString = CStdStringW;
#else
using CString = CStringA;
using CStdString = CStdStringA;
#endif
#define _HASH_SEED (size_t)0xdeadbeef
template<class _Kty>
inline size_t hash_value(const _Kty& _Keyval)
{
return ((size_t)_Keyval ^ _HASH_SEED);
}
template <class _InIt>
inline size_t _Hash_value(_InIt _Begin, _InIt _End)
{
size_t _Val = 2166136261U;
while(_Begin != _End)
_Val = 16777619U * _Val ^ (size_t)*_Begin++;
return (_Val);
}
template<class _Elem, class _Traits, class _Alloc>
inline size_t hash_value(const basic_string<_Elem, _Traits, _Alloc>& _Str)
{
const _Elem *_Ptr = _Str.c_str();
return (_Hash_value(_Ptr, _Ptr + _Str.size()));
}
template<class _Elem>
inline size_t hash_value(const CStringT<_Elem>& _Str)
{
const _Elem *_Ptr = _Str.c_str();
return (_Hash_value(_Ptr, _Ptr + _Str.size()));
}
inline size_t hash_value(const char *_Str)
{
return (_Hash_value(_Str, _Str + strlen(_Str)));
}
inline size_t hash_value(const wchar_t *_Str)
{
return (_Hash_value(_Str, _Str + wcslen(_Str)));
}

66
common/SysHelper.cpp Normal file
View File

@@ -0,0 +1,66 @@
/*
* Copyright: JessMA Open Source (ldcsaa@gmail.com)
*
* Author : Bruce Liang
* Website : https://github.com/ldcsaa
* Project : https://github.com/ldcsaa/HP-Socket
* Blog : http://www.cnblogs.com/ldcsaa
* Wiki : http://www.oschina.net/p/hp-socket
* QQ Group : 44636872, 75375912
*
* Licensed 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 "SysHelper.h"
#include <stdio.h>
#include <sys/utsname.h>
DWORD _GetKernelVersion()
{
utsname uts;
if(uname(&uts) == RS_FAIL)
return 0;
char c;
int major, minor, revise;
if(sscanf(uts.release, "%d.%d.%d%c", &major, &minor, &revise, &c) < 3)
return 0;
return (DWORD)((major << 16) | (minor << 8) | revise);
}
DWORD GetSysPageSize()
{
static const DWORD _s_page_size = (DWORD)SysGetPageSize();
return _s_page_size;
}
DWORD GetKernelVersion()
{
static const DWORD _s_kernel_version = _GetKernelVersion();
return _s_kernel_version;
}
BOOL IsKernelVersionAbove(BYTE major, BYTE minor, BYTE revise)
{
return GetKernelVersion() >= (DWORD)((major << 16) | (minor << 8) | revise);
}
DWORD GetDefaultWorkerThreadCount()
{
static const DWORD _s_dwtc = MIN((PROCESSOR_COUNT * 2 + 2), MAX_WORKER_THREAD_COUNT);
return _s_dwtc;
}

186
common/SysHelper.h Normal file
View File

@@ -0,0 +1,186 @@
/*
* Copyright: JessMA Open Source (ldcsaa@gmail.com)
*
* Author : Bruce Liang
* Website : https://github.com/ldcsaa
* Project : https://github.com/ldcsaa/HP-Socket
* Blog : http://www.cnblogs.com/ldcsaa
* Wiki : http://www.oschina.net/p/hp-socket
* QQ Group : 44636872, 75375912
*
* Licensed 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.
*/
#pragma once
#include "hpsocket/GlobalDef.h"
#include <unistd.h>
#include <sched.h>
#include <pthread.h>
#include <sys/syscall.h>
#include <sys/sysinfo.h>
using namespace std;
/* 最大工作线程数 */
#define MAX_WORKER_THREAD_COUNT 512
/* 默认对象缓存锁定时间 */
#define DEFAULT_OBJECT_CACHE_LOCK_TIME (30 * 1000)
/* 默认对象缓存池大小 */
#define DEFAULT_OBJECT_CACHE_POOL_SIZE 600
/* 默认对象缓存池回收阀值 */
#define DEFAULT_OBJECT_CACHE_POOL_HOLD 600
/* 默认内存块缓存容量 */
#define DEFAULT_BUFFER_CACHE_CAPACITY 4096
/* 默认内存块缓存池大小 */
#define DEFAULT_BUFFER_CACHE_POOL_SIZE 1024
/* 默认内存块缓存池回收阀值 */
#define DEFAULT_BUFFER_CACHE_POOL_HOLD 1024
/* 使用外部垃圾回收 */
#define USE_EXTERNAL_GC 1
#define SysGetSystemConfig sysconf
#define SysGetSystemInfo sysinfo
#if !defined(__ANDROID__)
#define SysGetPageSize getpagesize
#define SysGetNumberOfProcessors get_nprocs
#else
#define SysGetPageSize() sysconf(_SC_PAGESIZE)
#define SysGetNumberOfProcessors() sysconf(_SC_NPROCESSORS_ONLN)
#endif
#define SYS_PAGE_SIZE GetSysPageSize()
#define PROCESSOR_COUNT (::SysGetNumberOfProcessors())
#define GetCurrentProcessId getpid
#define SELF_PROCESS_ID (::GetCurrentProcessId())
#define gettid() syscall(__NR_gettid)
#define GetCurrentNativeThreadId() gettid()
#define SELF_NATIVE_THREAD_ID (::GetCurrentNativeThreadId())
#define GetCurrentThreadId pthread_self
#define SELF_THREAD_ID (::GetCurrentThreadId())
#define IsSameThread(tid1, tid2) pthread_equal((tid1), (tid2))
#define IsSelfThread(tid) IsSameThread((tid), SELF_THREAD_ID)
inline BOOL IsSameNativeThread(pid_t pid1, pid_t pid2)
{return (pid1 == pid2);}
#define IsSelfNativeThread(pid) IsSameNativeThread((pid), SELF_PROCESS_ID)
#define DEFAULT_WORKER_THREAD_COUNT GetDefaultWorkerThreadCount()
// Yield
#if defined(__cplusplus)
#include <thread>
static inline void __atomic_yield()
{
std::this_thread::yield();
}
#elif defined(_WIN32)
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
static inline void __atomic_yield()
{
YieldProcessor();
}
#elif defined(__SSE2__)
#include <emmintrin.h>
static inline void __atomic_yield()
{
_mm_pause();
}
#elif (defined(__GNUC__) || defined(__clang__)) && \
(defined(__x86_64__) || defined(__i386__) || defined(__arm__) || defined(__armel__) || defined(__ARMEL__) || \
defined(__aarch64__) || defined(__powerpc__) || defined(__ppc__) || defined(__PPC__))
#if defined(__x86_64__) || defined(__i386__)
static inline void __atomic_yield()
{
__asm__ volatile ("pause" ::: "memory");
}
#elif defined(__aarch64__)
static inline void __atomic_yield()
{
__asm__ volatile("wfe");
}
#elif (defined(__arm__) && __ARM_ARCH__ >= 7)
static inline void __atomic_yield()
{
__asm__ volatile("yield" ::: "memory");
}
#elif defined(__powerpc__) || defined(__ppc__) || defined(__PPC__)
static inline void __atomic_yield()
{
__asm__ __volatile__ ("or 27,27,27" ::: "memory");
}
#elif defined(__armel__) || defined(__ARMEL__)
static inline void __atomic_yield()
{
__asm__ volatile ("nop" ::: "memory");
}
#endif
#elif defined(__sun)
// Fallback for other archs
#include <synch.h>
static inline void __atomic_yield()
{
smt_pause();
}
#elif defined(__wasi__)
#include <sched.h>
static inline void __atomic_yield()
{
sched_yield();
}
#else
#include <unistd.h>
static inline void __atomic_yield()
{
sleep(0);
}
#endif // Yield
#define YieldProcessor __atomic_yield
#define SwitchToThread sched_yield
inline void __asm_nop() {__asm__ __volatile__("nop" : : : "memory");}
inline void __asm_rep_nop() {__asm__ __volatile__("rep; nop" : : : "memory");}
DWORD GetSysPageSize();
DWORD GetKernelVersion();
BOOL IsKernelVersionAbove(BYTE major, BYTE minor, BYTE revise);
DWORD GetDefaultWorkerThreadCount();
#if defined(__ANDROID__)
#include<android/api-level.h>
#if !defined(EFD_SEMAPHORE)
#define EFD_SEMAPHORE 00000001
#endif
#define pthread_cancel(t)
#if defined(__ANDROID_API__)
#if (__ANDROID_API__ < 21)
#define ppoll(fd, nfds, ptmspec, sig) poll((fd), (nfds), ((ptmspec) == nullptr) ? -1 : ((ptmspec)->tv_sec * 1000 + (ptmspec)->tv_nsec / 1000000))
#define epoll_create1(flag) epoll_create(32)
#define epoll_pwait(epfd, events, maxevents, timeout, sigmask) epoll_wait((epfd), (events), (maxevents), (timeout))
#endif
#endif
#endif

50
common/Thread.cpp Normal file
View File

@@ -0,0 +1,50 @@
/*
* Copyright: JessMA Open Source (ldcsaa@gmail.com)
*
* Author : Bruce Liang
* Website : https://github.com/ldcsaa
* Project : https://github.com/ldcsaa/HP-Socket
* Blog : http://www.cnblogs.com/ldcsaa
* Wiki : http://www.oschina.net/p/hp-socket
* QQ Group : 44636872, 75375912
*
* Licensed 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 "Thread.h"
static __thread BOOL s_tlsInterrupt = FALSE;
BOOL __CThread_Interrupt_::sm_bInitFlag = __CThread_Interrupt_::InitSigAction();
__CThread_Interrupt_::~__CThread_Interrupt_ () {s_tlsInterrupt = FALSE;}
BOOL __CThread_Interrupt_::IsInterrupted () {return s_tlsInterrupt;}
BOOL __CThread_Interrupt_::InitSigAction()
{
struct sigaction act;
sigemptyset(&act.sa_mask);
act.sa_handler = SignalHandler;
act.sa_flags = 0;
if(IS_HAS_ERROR(sigaction(SIG_NO_INTERRUPT, &act, nullptr)))
ERROR_ABORT();
return TRUE;
}
void __CThread_Interrupt_::SignalHandler(int sig)
{
if(sig == SIG_NO_INTERRUPT)
s_tlsInterrupt = TRUE;
}

612
common/Thread.h Normal file
View File

@@ -0,0 +1,612 @@
/*
* Copyright: JessMA Open Source (ldcsaa@gmail.com)
*
* Author : Bruce Liang
* Website : https://github.com/ldcsaa
* Project : https://github.com/ldcsaa/HP-Socket
* Blog : http://www.cnblogs.com/ldcsaa
* Wiki : http://www.oschina.net/p/hp-socket
* QQ Group : 44636872, 75375912
*
* Licensed 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.
*/
#pragma once
#include "hpsocket/GlobalDef.h"
#include "hpsocket/GlobalErrno.h"
#include "RWLock.h"
#include "STLHelper.h"
#include <pthread.h>
#include <signal.h>
#include <utility>
using namespace std;
/* Used to retry syscalls that can return EINTR. */
#define NO_EINTR_EXCEPT_THR_INTR(exp) ({ \
long int _rc; \
do {_rc = (long int)(exp);} \
while (IS_HAS_ERROR(_rc) && IS_INTR_ERROR() \
&& !::IsThreadInterrupted()); \
_rc; })
#define NO_EINTR_EXCEPT_THR_INTR_INT(exp) ((int)NO_EINTR_EXCEPT_THR_INTR(exp))
class __CThread_Interrupt_
{
public:
static const int SIG_NO_INTERRUPT = (_NSIG - 5);
private:
friend BOOL IsThreadInterrupted();
template<typename T, typename P, typename R> friend class CThread;
private:
static BOOL IsInterrupted();
static BOOL InitSigAction();
static void SignalHandler(int sig);
private:
~__CThread_Interrupt_();
private:
static BOOL sm_bInitFlag;
};
inline BOOL IsThreadInterrupted() {return __CThread_Interrupt_::IsInterrupted();}
class __CFakeRunnerClass_ {};
template<class T, class P = VOID, class R = UINT_PTR> class CThread
{
public:
using F = R (T::*)(P*);
using SF = R (*)(P*);
struct TWorker
{
CThread* m_pThread;
BOOL m_bDetach;
T* m_pRunner;
F m_pFunc;
P* m_pArg;
public:
TWorker(CThread* pThread, BOOL bDetach = FALSE, T* pRunner = nullptr, F pFunc = nullptr, P* pArg = nullptr)
: m_pThread(pThread)
{
Reset(bDetach, pRunner, pFunc, pArg);
}
void Reset(BOOL bDetach = FALSE, T* pRunner = nullptr, F pFunc = nullptr, P* pArg = nullptr)
{
m_bDetach = bDetach;
m_pRunner = pRunner;
m_pFunc = pFunc;
m_pArg = pArg;
}
public:
template<typename T_, typename R_, typename = enable_if_t<!is_same<T_, __CFakeRunnerClass_>::value && !is_void<R_>::value>>
PVOID Run(T_*, R_*)
{
return (PVOID)(UINT_PTR)((m_pRunner->*m_pFunc)(m_pArg));
}
template<typename T_, typename = enable_if_t<!is_same<T_, __CFakeRunnerClass_>::value>>
PVOID Run(T_*, PVOID)
{
(m_pRunner->*m_pFunc)(m_pArg);
return nullptr;
}
template<typename R_, typename = enable_if_t<!is_void<R_>::value>>
PVOID Run(__CFakeRunnerClass_*, R_*)
{
return (PVOID)(UINT_PTR)(*(SF*)&m_pFunc)(m_pArg);
}
PVOID Run(__CFakeRunnerClass_*, VOID*)
{
(*(SF*)&m_pFunc)(m_pArg);
return nullptr;
}
};
friend struct TWorker;
public:
BOOL Start(SF pFunc, P* pArg = nullptr, BOOL bDetach = FALSE, const pthread_attr_t* pAttr = nullptr)
{
return Start((__CFakeRunnerClass_*)nullptr, *(F*)&pFunc, pArg, bDetach, pAttr);
}
BOOL Start(T* pRunner, F pFunc, P* pArg = nullptr, BOOL bDetach = FALSE, const pthread_attr_t* pAttr = nullptr)
{
int rs = ERROR_INVALID_STATE;
if(IsRunning())
::SetLastError(rs);
else
{
m_Worker.Reset(bDetach, pRunner, pFunc, pArg);
SetRunning(TRUE);
rs = pthread_create(&m_ulThreadID, pAttr, ThreadProc, (PVOID)(&m_Worker));
if(rs != NO_ERROR)
{
Reset();
::SetLastError(rs);
}
}
return (rs == NO_ERROR);
}
#if !defined(__ANDROID__)
BOOL Cancel()
{
int rs = NO_ERROR;
if(!IsRunning() || ::IsSelfThread(m_ulThreadID))
rs = ERROR_INVALID_STATE;
else
rs = pthread_cancel(m_ulThreadID);
if(rs != NO_ERROR)
::SetLastError(rs);
return (rs == NO_ERROR);
}
BOOL Join(R* pResult = nullptr, BOOL bWait = TRUE, LONG lWaitMillsec = INFINITE)
{
int rs = NO_ERROR;
if(!IsRunning() || ::IsSelfThread(m_ulThreadID))
rs = ERROR_INVALID_STATE;
else
{
if(!bWait)
rs = pthread_tryjoin_np(m_ulThreadID, (PVOID*)pResult);
else if(IS_INFINITE(lWaitMillsec))
rs = pthread_join(m_ulThreadID, (PVOID*)pResult);
else
{
timespec ts;
::GetFutureTimespec(lWaitMillsec, ts, CLOCK_REALTIME);
rs = pthread_timedjoin_np(m_ulThreadID, (PVOID*)pResult, &ts);
}
}
if(rs == NO_ERROR)
SetRunning(FALSE);
else
::SetLastError(rs);
return (rs == NO_ERROR);
}
#else
BOOL Cancel()
{
SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
return FALSE;
}
BOOL Join(R* pResult = nullptr)
{
int rs = NO_ERROR;
if(!IsRunning() || ::IsSelfThread(m_ulThreadID))
rs = ERROR_INVALID_STATE;
else
rs = pthread_join(m_ulThreadID, (PVOID*)pResult);
if(rs == NO_ERROR)
SetRunning(FALSE);
else
::SetLastError(rs);
return (rs == NO_ERROR);
}
#endif
BOOL Detach()
{
int rs = NO_ERROR;
if(!IsRunning())
rs = ERROR_INVALID_STATE;
else
rs = pthread_detach(m_ulThreadID);
if(rs == NO_ERROR)
Reset();
else
::SetLastError(rs);
return (rs == NO_ERROR);
}
void Reset()
{
SetRunning(FALSE);
m_ulThreadID = 0;
m_lNativeID = 0;
m_Worker.Reset();
}
BOOL Interrupt()
{
if(!IsRunning())
{
SetLastError(ERROR_INVALID_STATE);
return FALSE;
}
return (IS_NO_ERROR(pthread_kill(m_ulThreadID, __CThread_Interrupt_::SIG_NO_INTERRUPT)));
}
void SetRunning(BOOL bRunning) {m_bRunning = bRunning;}
BOOL IsRunning () const {return m_bRunning;}
T* GetRunner () const {return m_Worker.m_pRunner;}
F GetFunc () const {return m_Worker.m_pFunc;}
SF GetSFunc () const {return *(SF*)&m_Worker.m_pFunc;}
P* GetArg () const {return m_Worker.m_pArg;}
THR_ID GetThreadID () const {return m_ulThreadID;}
NTHR_ID GetNativeID () const {return m_lNativeID;}
BOOL IsInMyThread () const {return IsMyThreadID(SELF_THREAD_ID);}
BOOL IsMyThreadID (THR_ID ulThreadID) const {return ::IsSameThread(ulThreadID, m_ulThreadID);}
BOOL IsMyNativeThreadID (NTHR_ID lNativeID) const {return ::IsSameNativeThread(lNativeID, m_lNativeID);}
private:
static PVOID ThreadProc(LPVOID pv)
{
UnmaskInterruptSignal();
__CThread_Interrupt_ tlsInterrupt;
TWorker* pWorker = (TWorker*)pv;
if(pWorker->m_bDetach)
pWorker->m_pThread->Detach();
else
pWorker->m_pThread->m_lNativeID = SELF_NATIVE_THREAD_ID;
PVOID pResult = pWorker->Run((T*)nullptr, (R*)nullptr);
return pResult;
}
static void UnmaskInterruptSignal()
{
sigset_t ss;
sigemptyset(&ss);
sigaddset(&ss, __CThread_Interrupt_::SIG_NO_INTERRUPT);
pthread_sigmask(SIG_UNBLOCK, &ss, nullptr);
}
public:
CThread()
: m_Worker(this)
{
Reset();
}
virtual ~CThread()
{
if(IsRunning())
{
Interrupt();
Join(nullptr);
}
ASSERT(!IsRunning());
}
DECLARE_NO_COPY_CLASS(CThread)
private:
THR_ID m_ulThreadID;
NTHR_ID m_lNativeID;
BOOL m_bRunning;
TWorker m_Worker;
};
template<class P = VOID, class R = UINT_PTR> using CStaticThread = CThread<__CFakeRunnerClass_, P, R>;
template<class T> class CTlsObj
{
using TLocalMap = unordered_map<THR_ID, T*>;
public:
T* TryGet()
{
T* pValue = nullptr;
{
CReadLock locallock(m_lock);
auto it = m_map.find(SELF_THREAD_ID);
if(it != m_map.end())
pValue = it->second;
}
return pValue;
}
template<typename ... _Con_Param> T* Get(_Con_Param&& ... construct_args)
{
T* pValue = TryGet();
if(pValue == nullptr)
{
pValue = Construct(forward<_Con_Param>(construct_args) ...);
CWriteLock locallock(m_lock);
m_map[SELF_THREAD_ID] = pValue;
}
return pValue;
}
template<typename ... _Con_Param> T& GetRef(_Con_Param&& ... construct_args)
{
return *Get(forward<_Con_Param>(construct_args) ...);
}
T* SetNewAndGetOld(T* pValue)
{
T* pOldValue = TryGet();
if(pValue != pOldValue)
{
if(pValue == nullptr)
DoRemove();
else
{
CWriteLock locallock(m_lock);
m_map[SELF_THREAD_ID] = pValue;
}
}
return pOldValue;
}
void Set(T* pValue)
{
T* pOldValue = SetNewAndGetOld(pValue);
if(pValue != pOldValue)
DoDelete(pOldValue);
}
void Remove()
{
T* pValue = TryGet();
if(pValue != nullptr)
{
DoDelete(pValue);
DoRemove();
}
}
void Clear()
{
CWriteLock locallock(m_lock);
if(!IsEmpty())
{
for(auto it = m_map.begin(), end = m_map.end(); it != end; ++it)
DoDelete(it->second);
m_map.clear();
}
}
TLocalMap& GetLocalMap() {return m_map;}
const TLocalMap& GetLocalMap() const {return m_map;}
CTlsObj& operator = (T* p) {Set(p); return *this;}
T* operator -> () {return Get();}
const T* operator -> () const {return Get();}
T& operator * () {return GetRef();}
const T& operator * () const {return GetRef();}
size_t Size () const {return m_map.size();}
bool IsEmpty() const {return m_map.empty();}
private:
inline void DoRemove()
{
CWriteLock locallock(m_lock);
m_map.erase(SELF_THREAD_ID);
}
static inline void DoDelete(T* pValue)
{
if(pValue != nullptr)
delete pValue;
}
template<typename ... _Con_Param> static inline T* Construct(_Con_Param&& ... construct_args)
{
return new T(forward<_Con_Param>(construct_args) ...);
}
public:
CTlsObj()
{
}
CTlsObj(T* pValue)
{
Set(pValue);
}
~CTlsObj()
{
Clear();
}
private:
CSimpleRWLock m_lock;
TLocalMap m_map;
DECLARE_NO_COPY_CLASS(CTlsObj)
};
template<class T> class CTlsSimple
{
using TLocalMap = unordered_map<THR_ID, T>;
static const T DEFAULT = (T)(0);
public:
BOOL TryGet(T& tValue)
{
BOOL isOK = FALSE;
{
CReadLock locallock(m_lock);
auto it = m_map.find(SELF_THREAD_ID);
if(it != m_map.end())
{
tValue = it->second;
isOK = TRUE;
}
}
return isOK;
}
T Get(T tDefault = DEFAULT)
{
T tValue;
if(TryGet(tValue))
return tValue;
Set(tDefault);
return tDefault;
}
T SetNewAndGetOld(T tValue)
{
T tOldValue;
if(!TryGet(tOldValue))
tOldValue = DEFAULT;
else if(tValue != tOldValue)
Set(tValue);
return tOldValue;
}
void Set(T tValue)
{
CWriteLock locallock(m_lock);
m_map[SELF_THREAD_ID] = tValue;
}
void Remove()
{
T tValue;
if(TryGet(tValue))
{
CWriteLock locallock(m_lock);
m_map.erase(SELF_THREAD_ID);
}
}
void Clear()
{
CWriteLock locallock(m_lock);
if(!IsEmpty())
m_map.clear();
}
TLocalMap& GetLocalMap() {return m_map;}
const TLocalMap& GetLocalMap() const {return m_map;}
CTlsSimple& operator = (T t) {Set(t); return *this;}
BOOL operator == (T t) {return Get() == t;}
BOOL operator != (T t) {return Get() != t;}
BOOL operator >= (T t) {return Get() >= t;}
BOOL operator <= (T t) {return Get() <= t;}
BOOL operator > (T t) {return Get() > t;}
BOOL operator < (T t) {return Get() < t;}
size_t Size () const {return m_map.size();}
bool IsEmpty() const {return m_map.empty();}
public:
CTlsSimple()
{
}
CTlsSimple(T tValue)
{
Set(tValue);
}
~CTlsSimple()
{
Clear();
}
DECLARE_NO_COPY_CLASS(CTlsSimple)
private:
CSimpleRWLock m_lock;
TLocalMap m_map;
};

11
common/http/Readme.txt Normal file
View File

@@ -0,0 +1,11 @@
http_parser Modifications
--------------------
1. move 'enum state' from http_parser.c to http_parser.h
3. http_parser.c ignore warning: "-Wconversion", "-Wsign-conversion"
llhttp Modifications
--------------------
1. llhttp_api.c ignore warning: "-Wconversion", "-Wsign-conversion"
1. llhttp_url.c ignore warning: "-Wconversion", "-Wsign-conversion"
2. llhttp_internal.c ignore warning: "-Wconversion", "-Wsign-conversion" "-Wunused-variable" "-Wunreachable-code"
3. llhttp.h, llhttp_url.h : LLHTTP_STRICT_MODE set default value 1

903
common/http/llhttp.h Normal file
View File

@@ -0,0 +1,903 @@
#ifndef INCLUDE_LLHTTP_H_
#define INCLUDE_LLHTTP_H_
#define LLHTTP_VERSION_MAJOR 9
#define LLHTTP_VERSION_MINOR 2
#define LLHTTP_VERSION_PATCH 1
#ifndef INCLUDE_LLHTTP_ITSELF_H_
#define INCLUDE_LLHTTP_ITSELF_H_
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
typedef struct llhttp__internal_s llhttp__internal_t;
struct llhttp__internal_s {
int32_t _index;
void* _span_pos0;
void* _span_cb0;
int32_t error;
const char* reason;
const char* error_pos;
void* data;
void* _current;
uint64_t content_length;
uint8_t type;
uint8_t method;
uint8_t http_major;
uint8_t http_minor;
uint8_t header_state;
uint16_t lenient_flags;
uint8_t upgrade;
uint8_t finish;
uint16_t flags;
uint16_t status_code;
uint8_t initial_message_completed;
void* settings;
};
int llhttp__internal_init(llhttp__internal_t* s);
int llhttp__internal_execute(llhttp__internal_t* s, const char* p, const char* endp);
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* INCLUDE_LLHTTP_ITSELF_H_ */
#ifndef LLLLHTTP_C_HEADERS_
#define LLLLHTTP_C_HEADERS_
#ifdef __cplusplus
extern "C" {
#endif
enum llhttp_errno {
HPE_OK = 0,
HPE_INTERNAL = 1,
HPE_STRICT = 2,
HPE_CR_EXPECTED = 25,
HPE_LF_EXPECTED = 3,
HPE_UNEXPECTED_CONTENT_LENGTH = 4,
HPE_UNEXPECTED_SPACE = 30,
HPE_CLOSED_CONNECTION = 5,
HPE_INVALID_METHOD = 6,
HPE_INVALID_URL = 7,
HPE_INVALID_CONSTANT = 8,
HPE_INVALID_VERSION = 9,
HPE_INVALID_HEADER_TOKEN = 10,
HPE_INVALID_CONTENT_LENGTH = 11,
HPE_INVALID_CHUNK_SIZE = 12,
HPE_INVALID_STATUS = 13,
HPE_INVALID_EOF_STATE = 14,
HPE_INVALID_TRANSFER_ENCODING = 15,
HPE_CB_MESSAGE_BEGIN = 16,
HPE_CB_HEADERS_COMPLETE = 17,
HPE_CB_MESSAGE_COMPLETE = 18,
HPE_CB_CHUNK_HEADER = 19,
HPE_CB_CHUNK_COMPLETE = 20,
HPE_PAUSED = 21,
HPE_PAUSED_UPGRADE = 22,
HPE_PAUSED_H2_UPGRADE = 23,
HPE_USER = 24,
HPE_CB_URL_COMPLETE = 26,
HPE_CB_STATUS_COMPLETE = 27,
HPE_CB_METHOD_COMPLETE = 32,
HPE_CB_VERSION_COMPLETE = 33,
HPE_CB_HEADER_FIELD_COMPLETE = 28,
HPE_CB_HEADER_VALUE_COMPLETE = 29,
HPE_CB_CHUNK_EXTENSION_NAME_COMPLETE = 34,
HPE_CB_CHUNK_EXTENSION_VALUE_COMPLETE = 35,
HPE_CB_RESET = 31
};
typedef enum llhttp_errno llhttp_errno_t;
enum llhttp_flags {
F_CONNECTION_KEEP_ALIVE = 0x1,
F_CONNECTION_CLOSE = 0x2,
F_CONNECTION_UPGRADE = 0x4,
F_CHUNKED = 0x8,
F_UPGRADE = 0x10,
F_CONTENT_LENGTH = 0x20,
F_SKIPBODY = 0x40,
F_TRAILING = 0x80,
F_TRANSFER_ENCODING = 0x200
};
typedef enum llhttp_flags llhttp_flags_t;
enum llhttp_lenient_flags {
LENIENT_HEADERS = 0x1,
LENIENT_CHUNKED_LENGTH = 0x2,
LENIENT_KEEP_ALIVE = 0x4,
LENIENT_TRANSFER_ENCODING = 0x8,
LENIENT_VERSION = 0x10,
LENIENT_DATA_AFTER_CLOSE = 0x20,
LENIENT_OPTIONAL_LF_AFTER_CR = 0x40,
LENIENT_OPTIONAL_CRLF_AFTER_CHUNK = 0x80,
LENIENT_OPTIONAL_CR_BEFORE_LF = 0x100,
LENIENT_SPACES_AFTER_CHUNK_SIZE = 0x200
};
typedef enum llhttp_lenient_flags llhttp_lenient_flags_t;
enum llhttp_type {
HTTP_BOTH = 0,
HTTP_REQUEST = 1,
HTTP_RESPONSE = 2
};
typedef enum llhttp_type llhttp_type_t;
enum llhttp_finish {
HTTP_FINISH_SAFE = 0,
HTTP_FINISH_SAFE_WITH_CB = 1,
HTTP_FINISH_UNSAFE = 2
};
typedef enum llhttp_finish llhttp_finish_t;
enum llhttp_method {
HTTP_DELETE = 0,
HTTP_GET = 1,
HTTP_HEAD = 2,
HTTP_POST = 3,
HTTP_PUT = 4,
HTTP_CONNECT = 5,
HTTP_OPTIONS = 6,
HTTP_TRACE = 7,
HTTP_COPY = 8,
HTTP_LOCK = 9,
HTTP_MKCOL = 10,
HTTP_MOVE = 11,
HTTP_PROPFIND = 12,
HTTP_PROPPATCH = 13,
HTTP_SEARCH = 14,
HTTP_UNLOCK = 15,
HTTP_BIND = 16,
HTTP_REBIND = 17,
HTTP_UNBIND = 18,
HTTP_ACL = 19,
HTTP_REPORT = 20,
HTTP_MKACTIVITY = 21,
HTTP_CHECKOUT = 22,
HTTP_MERGE = 23,
HTTP_MSEARCH = 24,
HTTP_NOTIFY = 25,
HTTP_SUBSCRIBE = 26,
HTTP_UNSUBSCRIBE = 27,
HTTP_PATCH = 28,
HTTP_PURGE = 29,
HTTP_MKCALENDAR = 30,
HTTP_LINK = 31,
HTTP_UNLINK = 32,
HTTP_SOURCE = 33,
HTTP_PRI = 34,
HTTP_DESCRIBE = 35,
HTTP_ANNOUNCE = 36,
HTTP_SETUP = 37,
HTTP_PLAY = 38,
HTTP_PAUSE = 39,
HTTP_TEARDOWN = 40,
HTTP_GET_PARAMETER = 41,
HTTP_SET_PARAMETER = 42,
HTTP_REDIRECT = 43,
HTTP_RECORD = 44,
HTTP_FLUSH = 45,
HTTP_QUERY = 46
};
typedef enum llhttp_method llhttp_method_t;
enum llhttp_status {
HTTP_STATUS_CONTINUE = 100,
HTTP_STATUS_SWITCHING_PROTOCOLS = 101,
HTTP_STATUS_PROCESSING = 102,
HTTP_STATUS_EARLY_HINTS = 103,
HTTP_STATUS_RESPONSE_IS_STALE = 110,
HTTP_STATUS_REVALIDATION_FAILED = 111,
HTTP_STATUS_DISCONNECTED_OPERATION = 112,
HTTP_STATUS_HEURISTIC_EXPIRATION = 113,
HTTP_STATUS_MISCELLANEOUS_WARNING = 199,
HTTP_STATUS_OK = 200,
HTTP_STATUS_CREATED = 201,
HTTP_STATUS_ACCEPTED = 202,
HTTP_STATUS_NON_AUTHORITATIVE_INFORMATION = 203,
HTTP_STATUS_NO_CONTENT = 204,
HTTP_STATUS_RESET_CONTENT = 205,
HTTP_STATUS_PARTIAL_CONTENT = 206,
HTTP_STATUS_MULTI_STATUS = 207,
HTTP_STATUS_ALREADY_REPORTED = 208,
HTTP_STATUS_TRANSFORMATION_APPLIED = 214,
HTTP_STATUS_IM_USED = 226,
HTTP_STATUS_MISCELLANEOUS_PERSISTENT_WARNING = 299,
HTTP_STATUS_MULTIPLE_CHOICES = 300,
HTTP_STATUS_MOVED_PERMANENTLY = 301,
HTTP_STATUS_FOUND = 302,
HTTP_STATUS_SEE_OTHER = 303,
HTTP_STATUS_NOT_MODIFIED = 304,
HTTP_STATUS_USE_PROXY = 305,
HTTP_STATUS_SWITCH_PROXY = 306,
HTTP_STATUS_TEMPORARY_REDIRECT = 307,
HTTP_STATUS_PERMANENT_REDIRECT = 308,
HTTP_STATUS_BAD_REQUEST = 400,
HTTP_STATUS_UNAUTHORIZED = 401,
HTTP_STATUS_PAYMENT_REQUIRED = 402,
HTTP_STATUS_FORBIDDEN = 403,
HTTP_STATUS_NOT_FOUND = 404,
HTTP_STATUS_METHOD_NOT_ALLOWED = 405,
HTTP_STATUS_NOT_ACCEPTABLE = 406,
HTTP_STATUS_PROXY_AUTHENTICATION_REQUIRED = 407,
HTTP_STATUS_REQUEST_TIMEOUT = 408,
HTTP_STATUS_CONFLICT = 409,
HTTP_STATUS_GONE = 410,
HTTP_STATUS_LENGTH_REQUIRED = 411,
HTTP_STATUS_PRECONDITION_FAILED = 412,
HTTP_STATUS_PAYLOAD_TOO_LARGE = 413,
HTTP_STATUS_URI_TOO_LONG = 414,
HTTP_STATUS_UNSUPPORTED_MEDIA_TYPE = 415,
HTTP_STATUS_RANGE_NOT_SATISFIABLE = 416,
HTTP_STATUS_EXPECTATION_FAILED = 417,
HTTP_STATUS_IM_A_TEAPOT = 418,
HTTP_STATUS_PAGE_EXPIRED = 419,
HTTP_STATUS_ENHANCE_YOUR_CALM = 420,
HTTP_STATUS_MISDIRECTED_REQUEST = 421,
HTTP_STATUS_UNPROCESSABLE_ENTITY = 422,
HTTP_STATUS_LOCKED = 423,
HTTP_STATUS_FAILED_DEPENDENCY = 424,
HTTP_STATUS_TOO_EARLY = 425,
HTTP_STATUS_UPGRADE_REQUIRED = 426,
HTTP_STATUS_PRECONDITION_REQUIRED = 428,
HTTP_STATUS_TOO_MANY_REQUESTS = 429,
HTTP_STATUS_REQUEST_HEADER_FIELDS_TOO_LARGE_UNOFFICIAL = 430,
HTTP_STATUS_REQUEST_HEADER_FIELDS_TOO_LARGE = 431,
HTTP_STATUS_LOGIN_TIMEOUT = 440,
HTTP_STATUS_NO_RESPONSE = 444,
HTTP_STATUS_RETRY_WITH = 449,
HTTP_STATUS_BLOCKED_BY_PARENTAL_CONTROL = 450,
HTTP_STATUS_UNAVAILABLE_FOR_LEGAL_REASONS = 451,
HTTP_STATUS_CLIENT_CLOSED_LOAD_BALANCED_REQUEST = 460,
HTTP_STATUS_INVALID_X_FORWARDED_FOR = 463,
HTTP_STATUS_REQUEST_HEADER_TOO_LARGE = 494,
HTTP_STATUS_SSL_CERTIFICATE_ERROR = 495,
HTTP_STATUS_SSL_CERTIFICATE_REQUIRED = 496,
HTTP_STATUS_HTTP_REQUEST_SENT_TO_HTTPS_PORT = 497,
HTTP_STATUS_INVALID_TOKEN = 498,
HTTP_STATUS_CLIENT_CLOSED_REQUEST = 499,
HTTP_STATUS_INTERNAL_SERVER_ERROR = 500,
HTTP_STATUS_NOT_IMPLEMENTED = 501,
HTTP_STATUS_BAD_GATEWAY = 502,
HTTP_STATUS_SERVICE_UNAVAILABLE = 503,
HTTP_STATUS_GATEWAY_TIMEOUT = 504,
HTTP_STATUS_HTTP_VERSION_NOT_SUPPORTED = 505,
HTTP_STATUS_VARIANT_ALSO_NEGOTIATES = 506,
HTTP_STATUS_INSUFFICIENT_STORAGE = 507,
HTTP_STATUS_LOOP_DETECTED = 508,
HTTP_STATUS_BANDWIDTH_LIMIT_EXCEEDED = 509,
HTTP_STATUS_NOT_EXTENDED = 510,
HTTP_STATUS_NETWORK_AUTHENTICATION_REQUIRED = 511,
HTTP_STATUS_WEB_SERVER_UNKNOWN_ERROR = 520,
HTTP_STATUS_WEB_SERVER_IS_DOWN = 521,
HTTP_STATUS_CONNECTION_TIMEOUT = 522,
HTTP_STATUS_ORIGIN_IS_UNREACHABLE = 523,
HTTP_STATUS_TIMEOUT_OCCURED = 524,
HTTP_STATUS_SSL_HANDSHAKE_FAILED = 525,
HTTP_STATUS_INVALID_SSL_CERTIFICATE = 526,
HTTP_STATUS_RAILGUN_ERROR = 527,
HTTP_STATUS_SITE_IS_OVERLOADED = 529,
HTTP_STATUS_SITE_IS_FROZEN = 530,
HTTP_STATUS_IDENTITY_PROVIDER_AUTHENTICATION_ERROR = 561,
HTTP_STATUS_NETWORK_READ_TIMEOUT = 598,
HTTP_STATUS_NETWORK_CONNECT_TIMEOUT = 599
};
typedef enum llhttp_status llhttp_status_t;
#define HTTP_ERRNO_MAP(XX) \
XX(0, OK, OK) \
XX(1, INTERNAL, INTERNAL) \
XX(2, STRICT, STRICT) \
XX(25, CR_EXPECTED, CR_EXPECTED) \
XX(3, LF_EXPECTED, LF_EXPECTED) \
XX(4, UNEXPECTED_CONTENT_LENGTH, UNEXPECTED_CONTENT_LENGTH) \
XX(30, UNEXPECTED_SPACE, UNEXPECTED_SPACE) \
XX(5, CLOSED_CONNECTION, CLOSED_CONNECTION) \
XX(6, INVALID_METHOD, INVALID_METHOD) \
XX(7, INVALID_URL, INVALID_URL) \
XX(8, INVALID_CONSTANT, INVALID_CONSTANT) \
XX(9, INVALID_VERSION, INVALID_VERSION) \
XX(10, INVALID_HEADER_TOKEN, INVALID_HEADER_TOKEN) \
XX(11, INVALID_CONTENT_LENGTH, INVALID_CONTENT_LENGTH) \
XX(12, INVALID_CHUNK_SIZE, INVALID_CHUNK_SIZE) \
XX(13, INVALID_STATUS, INVALID_STATUS) \
XX(14, INVALID_EOF_STATE, INVALID_EOF_STATE) \
XX(15, INVALID_TRANSFER_ENCODING, INVALID_TRANSFER_ENCODING) \
XX(16, CB_MESSAGE_BEGIN, CB_MESSAGE_BEGIN) \
XX(17, CB_HEADERS_COMPLETE, CB_HEADERS_COMPLETE) \
XX(18, CB_MESSAGE_COMPLETE, CB_MESSAGE_COMPLETE) \
XX(19, CB_CHUNK_HEADER, CB_CHUNK_HEADER) \
XX(20, CB_CHUNK_COMPLETE, CB_CHUNK_COMPLETE) \
XX(21, PAUSED, PAUSED) \
XX(22, PAUSED_UPGRADE, PAUSED_UPGRADE) \
XX(23, PAUSED_H2_UPGRADE, PAUSED_H2_UPGRADE) \
XX(24, USER, USER) \
XX(26, CB_URL_COMPLETE, CB_URL_COMPLETE) \
XX(27, CB_STATUS_COMPLETE, CB_STATUS_COMPLETE) \
XX(32, CB_METHOD_COMPLETE, CB_METHOD_COMPLETE) \
XX(33, CB_VERSION_COMPLETE, CB_VERSION_COMPLETE) \
XX(28, CB_HEADER_FIELD_COMPLETE, CB_HEADER_FIELD_COMPLETE) \
XX(29, CB_HEADER_VALUE_COMPLETE, CB_HEADER_VALUE_COMPLETE) \
XX(34, CB_CHUNK_EXTENSION_NAME_COMPLETE, CB_CHUNK_EXTENSION_NAME_COMPLETE) \
XX(35, CB_CHUNK_EXTENSION_VALUE_COMPLETE, CB_CHUNK_EXTENSION_VALUE_COMPLETE) \
XX(31, CB_RESET, CB_RESET) \
#define HTTP_METHOD_MAP(XX) \
XX(0, DELETE, DELETE) \
XX(1, GET, GET) \
XX(2, HEAD, HEAD) \
XX(3, POST, POST) \
XX(4, PUT, PUT) \
XX(5, CONNECT, CONNECT) \
XX(6, OPTIONS, OPTIONS) \
XX(7, TRACE, TRACE) \
XX(8, COPY, COPY) \
XX(9, LOCK, LOCK) \
XX(10, MKCOL, MKCOL) \
XX(11, MOVE, MOVE) \
XX(12, PROPFIND, PROPFIND) \
XX(13, PROPPATCH, PROPPATCH) \
XX(14, SEARCH, SEARCH) \
XX(15, UNLOCK, UNLOCK) \
XX(16, BIND, BIND) \
XX(17, REBIND, REBIND) \
XX(18, UNBIND, UNBIND) \
XX(19, ACL, ACL) \
XX(20, REPORT, REPORT) \
XX(21, MKACTIVITY, MKACTIVITY) \
XX(22, CHECKOUT, CHECKOUT) \
XX(23, MERGE, MERGE) \
XX(24, MSEARCH, M-SEARCH) \
XX(25, NOTIFY, NOTIFY) \
XX(26, SUBSCRIBE, SUBSCRIBE) \
XX(27, UNSUBSCRIBE, UNSUBSCRIBE) \
XX(28, PATCH, PATCH) \
XX(29, PURGE, PURGE) \
XX(30, MKCALENDAR, MKCALENDAR) \
XX(31, LINK, LINK) \
XX(32, UNLINK, UNLINK) \
XX(33, SOURCE, SOURCE) \
XX(46, QUERY, QUERY) \
#define RTSP_METHOD_MAP(XX) \
XX(1, GET, GET) \
XX(3, POST, POST) \
XX(6, OPTIONS, OPTIONS) \
XX(35, DESCRIBE, DESCRIBE) \
XX(36, ANNOUNCE, ANNOUNCE) \
XX(37, SETUP, SETUP) \
XX(38, PLAY, PLAY) \
XX(39, PAUSE, PAUSE) \
XX(40, TEARDOWN, TEARDOWN) \
XX(41, GET_PARAMETER, GET_PARAMETER) \
XX(42, SET_PARAMETER, SET_PARAMETER) \
XX(43, REDIRECT, REDIRECT) \
XX(44, RECORD, RECORD) \
XX(45, FLUSH, FLUSH) \
#define HTTP_ALL_METHOD_MAP(XX) \
XX(0, DELETE, DELETE) \
XX(1, GET, GET) \
XX(2, HEAD, HEAD) \
XX(3, POST, POST) \
XX(4, PUT, PUT) \
XX(5, CONNECT, CONNECT) \
XX(6, OPTIONS, OPTIONS) \
XX(7, TRACE, TRACE) \
XX(8, COPY, COPY) \
XX(9, LOCK, LOCK) \
XX(10, MKCOL, MKCOL) \
XX(11, MOVE, MOVE) \
XX(12, PROPFIND, PROPFIND) \
XX(13, PROPPATCH, PROPPATCH) \
XX(14, SEARCH, SEARCH) \
XX(15, UNLOCK, UNLOCK) \
XX(16, BIND, BIND) \
XX(17, REBIND, REBIND) \
XX(18, UNBIND, UNBIND) \
XX(19, ACL, ACL) \
XX(20, REPORT, REPORT) \
XX(21, MKACTIVITY, MKACTIVITY) \
XX(22, CHECKOUT, CHECKOUT) \
XX(23, MERGE, MERGE) \
XX(24, MSEARCH, M-SEARCH) \
XX(25, NOTIFY, NOTIFY) \
XX(26, SUBSCRIBE, SUBSCRIBE) \
XX(27, UNSUBSCRIBE, UNSUBSCRIBE) \
XX(28, PATCH, PATCH) \
XX(29, PURGE, PURGE) \
XX(30, MKCALENDAR, MKCALENDAR) \
XX(31, LINK, LINK) \
XX(32, UNLINK, UNLINK) \
XX(33, SOURCE, SOURCE) \
XX(34, PRI, PRI) \
XX(35, DESCRIBE, DESCRIBE) \
XX(36, ANNOUNCE, ANNOUNCE) \
XX(37, SETUP, SETUP) \
XX(38, PLAY, PLAY) \
XX(39, PAUSE, PAUSE) \
XX(40, TEARDOWN, TEARDOWN) \
XX(41, GET_PARAMETER, GET_PARAMETER) \
XX(42, SET_PARAMETER, SET_PARAMETER) \
XX(43, REDIRECT, REDIRECT) \
XX(44, RECORD, RECORD) \
XX(45, FLUSH, FLUSH) \
XX(46, QUERY, QUERY) \
#define HTTP_STATUS_MAP(XX) \
XX(100, CONTINUE, CONTINUE) \
XX(101, SWITCHING_PROTOCOLS, SWITCHING_PROTOCOLS) \
XX(102, PROCESSING, PROCESSING) \
XX(103, EARLY_HINTS, EARLY_HINTS) \
XX(110, RESPONSE_IS_STALE, RESPONSE_IS_STALE) \
XX(111, REVALIDATION_FAILED, REVALIDATION_FAILED) \
XX(112, DISCONNECTED_OPERATION, DISCONNECTED_OPERATION) \
XX(113, HEURISTIC_EXPIRATION, HEURISTIC_EXPIRATION) \
XX(199, MISCELLANEOUS_WARNING, MISCELLANEOUS_WARNING) \
XX(200, OK, OK) \
XX(201, CREATED, CREATED) \
XX(202, ACCEPTED, ACCEPTED) \
XX(203, NON_AUTHORITATIVE_INFORMATION, NON_AUTHORITATIVE_INFORMATION) \
XX(204, NO_CONTENT, NO_CONTENT) \
XX(205, RESET_CONTENT, RESET_CONTENT) \
XX(206, PARTIAL_CONTENT, PARTIAL_CONTENT) \
XX(207, MULTI_STATUS, MULTI_STATUS) \
XX(208, ALREADY_REPORTED, ALREADY_REPORTED) \
XX(214, TRANSFORMATION_APPLIED, TRANSFORMATION_APPLIED) \
XX(226, IM_USED, IM_USED) \
XX(299, MISCELLANEOUS_PERSISTENT_WARNING, MISCELLANEOUS_PERSISTENT_WARNING) \
XX(300, MULTIPLE_CHOICES, MULTIPLE_CHOICES) \
XX(301, MOVED_PERMANENTLY, MOVED_PERMANENTLY) \
XX(302, FOUND, FOUND) \
XX(303, SEE_OTHER, SEE_OTHER) \
XX(304, NOT_MODIFIED, NOT_MODIFIED) \
XX(305, USE_PROXY, USE_PROXY) \
XX(306, SWITCH_PROXY, SWITCH_PROXY) \
XX(307, TEMPORARY_REDIRECT, TEMPORARY_REDIRECT) \
XX(308, PERMANENT_REDIRECT, PERMANENT_REDIRECT) \
XX(400, BAD_REQUEST, BAD_REQUEST) \
XX(401, UNAUTHORIZED, UNAUTHORIZED) \
XX(402, PAYMENT_REQUIRED, PAYMENT_REQUIRED) \
XX(403, FORBIDDEN, FORBIDDEN) \
XX(404, NOT_FOUND, NOT_FOUND) \
XX(405, METHOD_NOT_ALLOWED, METHOD_NOT_ALLOWED) \
XX(406, NOT_ACCEPTABLE, NOT_ACCEPTABLE) \
XX(407, PROXY_AUTHENTICATION_REQUIRED, PROXY_AUTHENTICATION_REQUIRED) \
XX(408, REQUEST_TIMEOUT, REQUEST_TIMEOUT) \
XX(409, CONFLICT, CONFLICT) \
XX(410, GONE, GONE) \
XX(411, LENGTH_REQUIRED, LENGTH_REQUIRED) \
XX(412, PRECONDITION_FAILED, PRECONDITION_FAILED) \
XX(413, PAYLOAD_TOO_LARGE, PAYLOAD_TOO_LARGE) \
XX(414, URI_TOO_LONG, URI_TOO_LONG) \
XX(415, UNSUPPORTED_MEDIA_TYPE, UNSUPPORTED_MEDIA_TYPE) \
XX(416, RANGE_NOT_SATISFIABLE, RANGE_NOT_SATISFIABLE) \
XX(417, EXPECTATION_FAILED, EXPECTATION_FAILED) \
XX(418, IM_A_TEAPOT, IM_A_TEAPOT) \
XX(419, PAGE_EXPIRED, PAGE_EXPIRED) \
XX(420, ENHANCE_YOUR_CALM, ENHANCE_YOUR_CALM) \
XX(421, MISDIRECTED_REQUEST, MISDIRECTED_REQUEST) \
XX(422, UNPROCESSABLE_ENTITY, UNPROCESSABLE_ENTITY) \
XX(423, LOCKED, LOCKED) \
XX(424, FAILED_DEPENDENCY, FAILED_DEPENDENCY) \
XX(425, TOO_EARLY, TOO_EARLY) \
XX(426, UPGRADE_REQUIRED, UPGRADE_REQUIRED) \
XX(428, PRECONDITION_REQUIRED, PRECONDITION_REQUIRED) \
XX(429, TOO_MANY_REQUESTS, TOO_MANY_REQUESTS) \
XX(430, REQUEST_HEADER_FIELDS_TOO_LARGE_UNOFFICIAL, REQUEST_HEADER_FIELDS_TOO_LARGE_UNOFFICIAL) \
XX(431, REQUEST_HEADER_FIELDS_TOO_LARGE, REQUEST_HEADER_FIELDS_TOO_LARGE) \
XX(440, LOGIN_TIMEOUT, LOGIN_TIMEOUT) \
XX(444, NO_RESPONSE, NO_RESPONSE) \
XX(449, RETRY_WITH, RETRY_WITH) \
XX(450, BLOCKED_BY_PARENTAL_CONTROL, BLOCKED_BY_PARENTAL_CONTROL) \
XX(451, UNAVAILABLE_FOR_LEGAL_REASONS, UNAVAILABLE_FOR_LEGAL_REASONS) \
XX(460, CLIENT_CLOSED_LOAD_BALANCED_REQUEST, CLIENT_CLOSED_LOAD_BALANCED_REQUEST) \
XX(463, INVALID_X_FORWARDED_FOR, INVALID_X_FORWARDED_FOR) \
XX(494, REQUEST_HEADER_TOO_LARGE, REQUEST_HEADER_TOO_LARGE) \
XX(495, SSL_CERTIFICATE_ERROR, SSL_CERTIFICATE_ERROR) \
XX(496, SSL_CERTIFICATE_REQUIRED, SSL_CERTIFICATE_REQUIRED) \
XX(497, HTTP_REQUEST_SENT_TO_HTTPS_PORT, HTTP_REQUEST_SENT_TO_HTTPS_PORT) \
XX(498, INVALID_TOKEN, INVALID_TOKEN) \
XX(499, CLIENT_CLOSED_REQUEST, CLIENT_CLOSED_REQUEST) \
XX(500, INTERNAL_SERVER_ERROR, INTERNAL_SERVER_ERROR) \
XX(501, NOT_IMPLEMENTED, NOT_IMPLEMENTED) \
XX(502, BAD_GATEWAY, BAD_GATEWAY) \
XX(503, SERVICE_UNAVAILABLE, SERVICE_UNAVAILABLE) \
XX(504, GATEWAY_TIMEOUT, GATEWAY_TIMEOUT) \
XX(505, HTTP_VERSION_NOT_SUPPORTED, HTTP_VERSION_NOT_SUPPORTED) \
XX(506, VARIANT_ALSO_NEGOTIATES, VARIANT_ALSO_NEGOTIATES) \
XX(507, INSUFFICIENT_STORAGE, INSUFFICIENT_STORAGE) \
XX(508, LOOP_DETECTED, LOOP_DETECTED) \
XX(509, BANDWIDTH_LIMIT_EXCEEDED, BANDWIDTH_LIMIT_EXCEEDED) \
XX(510, NOT_EXTENDED, NOT_EXTENDED) \
XX(511, NETWORK_AUTHENTICATION_REQUIRED, NETWORK_AUTHENTICATION_REQUIRED) \
XX(520, WEB_SERVER_UNKNOWN_ERROR, WEB_SERVER_UNKNOWN_ERROR) \
XX(521, WEB_SERVER_IS_DOWN, WEB_SERVER_IS_DOWN) \
XX(522, CONNECTION_TIMEOUT, CONNECTION_TIMEOUT) \
XX(523, ORIGIN_IS_UNREACHABLE, ORIGIN_IS_UNREACHABLE) \
XX(524, TIMEOUT_OCCURED, TIMEOUT_OCCURED) \
XX(525, SSL_HANDSHAKE_FAILED, SSL_HANDSHAKE_FAILED) \
XX(526, INVALID_SSL_CERTIFICATE, INVALID_SSL_CERTIFICATE) \
XX(527, RAILGUN_ERROR, RAILGUN_ERROR) \
XX(529, SITE_IS_OVERLOADED, SITE_IS_OVERLOADED) \
XX(530, SITE_IS_FROZEN, SITE_IS_FROZEN) \
XX(561, IDENTITY_PROVIDER_AUTHENTICATION_ERROR, IDENTITY_PROVIDER_AUTHENTICATION_ERROR) \
XX(598, NETWORK_READ_TIMEOUT, NETWORK_READ_TIMEOUT) \
XX(599, NETWORK_CONNECT_TIMEOUT, NETWORK_CONNECT_TIMEOUT) \
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* LLLLHTTP_C_HEADERS_ */
#ifndef INCLUDE_LLHTTP_API_H_
#define INCLUDE_LLHTTP_API_H_
#ifdef __cplusplus
extern "C" {
#endif
#include <stddef.h>
#if defined(__wasm__)
#define LLHTTP_EXPORT __attribute__((visibility("default")))
//#elif defined(_WIN32)
//#define LLHTTP_EXPORT __declspec(dllexport)
#else
#define LLHTTP_EXPORT
#endif
typedef llhttp__internal_t llhttp_t;
typedef struct llhttp_settings_s llhttp_settings_t;
typedef int (*llhttp_data_cb)(llhttp_t*, const char *at, size_t length);
typedef int (*llhttp_cb)(llhttp_t*);
struct llhttp_settings_s {
/* Possible return values 0, -1, `HPE_PAUSED` */
llhttp_cb on_message_begin;
/* Possible return values 0, -1, HPE_USER */
llhttp_data_cb on_url;
llhttp_data_cb on_status;
llhttp_data_cb on_method;
llhttp_data_cb on_version;
llhttp_data_cb on_header_field;
llhttp_data_cb on_header_value;
llhttp_data_cb on_chunk_extension_name;
llhttp_data_cb on_chunk_extension_value;
/* Possible return values:
* 0 - Proceed normally
* 1 - Assume that request/response has no body, and proceed to parsing the
* next message
* 2 - Assume absence of body (as above) and make `llhttp_execute()` return
* `HPE_PAUSED_UPGRADE`
* -1 - Error
* `HPE_PAUSED`
*/
llhttp_cb on_headers_complete;
/* Possible return values 0, -1, HPE_USER */
llhttp_data_cb on_body;
/* Possible return values 0, -1, `HPE_PAUSED` */
llhttp_cb on_message_complete;
llhttp_cb on_url_complete;
llhttp_cb on_status_complete;
llhttp_cb on_method_complete;
llhttp_cb on_version_complete;
llhttp_cb on_header_field_complete;
llhttp_cb on_header_value_complete;
llhttp_cb on_chunk_extension_name_complete;
llhttp_cb on_chunk_extension_value_complete;
/* When on_chunk_header is called, the current chunk length is stored
* in parser->content_length.
* Possible return values 0, -1, `HPE_PAUSED`
*/
llhttp_cb on_chunk_header;
llhttp_cb on_chunk_complete;
llhttp_cb on_reset;
};
/* Initialize the parser with specific type and user settings.
*
* NOTE: lifetime of `settings` has to be at least the same as the lifetime of
* the `parser` here. In practice, `settings` has to be either a static
* variable or be allocated with `malloc`, `new`, etc.
*/
LLHTTP_EXPORT
void llhttp_init(llhttp_t* parser, llhttp_type_t type,
const llhttp_settings_t* settings);
LLHTTP_EXPORT
llhttp_t* llhttp_alloc(llhttp_type_t type);
LLHTTP_EXPORT
void llhttp_free(llhttp_t* parser);
LLHTTP_EXPORT
uint8_t llhttp_get_type(llhttp_t* parser);
LLHTTP_EXPORT
uint8_t llhttp_get_http_major(llhttp_t* parser);
LLHTTP_EXPORT
uint8_t llhttp_get_http_minor(llhttp_t* parser);
LLHTTP_EXPORT
uint8_t llhttp_get_method(llhttp_t* parser);
LLHTTP_EXPORT
int llhttp_get_status_code(llhttp_t* parser);
LLHTTP_EXPORT
uint8_t llhttp_get_upgrade(llhttp_t* parser);
/* Reset an already initialized parser back to the start state, preserving the
* existing parser type, callback settings, user data, and lenient flags.
*/
LLHTTP_EXPORT
void llhttp_reset(llhttp_t* parser);
/* Initialize the settings object */
LLHTTP_EXPORT
void llhttp_settings_init(llhttp_settings_t* settings);
/* Parse full or partial request/response, invoking user callbacks along the
* way.
*
* If any of `llhttp_data_cb` returns errno not equal to `HPE_OK` - the parsing
* interrupts, and such errno is returned from `llhttp_execute()`. If
* `HPE_PAUSED` was used as a errno, the execution can be resumed with
* `llhttp_resume()` call.
*
* In a special case of CONNECT/Upgrade request/response `HPE_PAUSED_UPGRADE`
* is returned after fully parsing the request/response. If the user wishes to
* continue parsing, they need to invoke `llhttp_resume_after_upgrade()`.
*
* NOTE: if this function ever returns a non-pause type error, it will continue
* to return the same error upon each successive call up until `llhttp_init()`
* is called.
*/
LLHTTP_EXPORT
llhttp_errno_t llhttp_execute(llhttp_t* parser, const char* data, size_t len);
/* This method should be called when the other side has no further bytes to
* send (e.g. shutdown of readable side of the TCP connection.)
*
* Requests without `Content-Length` and other messages might require treating
* all incoming bytes as the part of the body, up to the last byte of the
* connection. This method will invoke `on_message_complete()` callback if the
* request was terminated safely. Otherwise a error code would be returned.
*/
LLHTTP_EXPORT
llhttp_errno_t llhttp_finish(llhttp_t* parser);
/* Returns `1` if the incoming message is parsed until the last byte, and has
* to be completed by calling `llhttp_finish()` on EOF
*/
LLHTTP_EXPORT
int llhttp_message_needs_eof(const llhttp_t* parser);
/* Returns `1` if there might be any other messages following the last that was
* successfully parsed.
*/
LLHTTP_EXPORT
int llhttp_should_keep_alive(const llhttp_t* parser);
/* Make further calls of `llhttp_execute()` return `HPE_PAUSED` and set
* appropriate error reason.
*
* Important: do not call this from user callbacks! User callbacks must return
* `HPE_PAUSED` if pausing is required.
*/
LLHTTP_EXPORT
void llhttp_pause(llhttp_t* parser);
/* Might be called to resume the execution after the pause in user's callback.
* See `llhttp_execute()` above for details.
*
* Call this only if `llhttp_execute()` returns `HPE_PAUSED`.
*/
LLHTTP_EXPORT
void llhttp_resume(llhttp_t* parser);
/* Might be called to resume the execution after the pause in user's callback.
* See `llhttp_execute()` above for details.
*
* Call this only if `llhttp_execute()` returns `HPE_PAUSED_UPGRADE`
*/
LLHTTP_EXPORT
void llhttp_resume_after_upgrade(llhttp_t* parser);
/* Returns the latest return error */
LLHTTP_EXPORT
llhttp_errno_t llhttp_get_errno(const llhttp_t* parser);
/* Returns the verbal explanation of the latest returned error.
*
* Note: User callback should set error reason when returning the error. See
* `llhttp_set_error_reason()` for details.
*/
LLHTTP_EXPORT
const char* llhttp_get_error_reason(const llhttp_t* parser);
/* Assign verbal description to the returned error. Must be called in user
* callbacks right before returning the errno.
*
* Note: `HPE_USER` error code might be useful in user callbacks.
*/
LLHTTP_EXPORT
void llhttp_set_error_reason(llhttp_t* parser, const char* reason);
/* Returns the pointer to the last parsed byte before the returned error. The
* pointer is relative to the `data` argument of `llhttp_execute()`.
*
* Note: this method might be useful for counting the number of parsed bytes.
*/
LLHTTP_EXPORT
const char* llhttp_get_error_pos(const llhttp_t* parser);
/* Returns textual name of error code */
LLHTTP_EXPORT
const char* llhttp_errno_name(llhttp_errno_t err);
/* Returns textual name of HTTP method */
LLHTTP_EXPORT
const char* llhttp_method_name(llhttp_method_t method);
/* Returns textual name of HTTP status */
LLHTTP_EXPORT
const char* llhttp_status_name(llhttp_status_t status);
/* Enables/disables lenient header value parsing (disabled by default).
*
* Lenient parsing disables header value token checks, extending llhttp's
* protocol support to highly non-compliant clients/server. No
* `HPE_INVALID_HEADER_TOKEN` will be raised for incorrect header values when
* lenient parsing is "on".
*
* **Enabling this flag can pose a security issue since you will be exposed to
* request smuggling attacks. USE WITH CAUTION!**
*/
LLHTTP_EXPORT
void llhttp_set_lenient_headers(llhttp_t* parser, int enabled);
/* Enables/disables lenient handling of conflicting `Transfer-Encoding` and
* `Content-Length` headers (disabled by default).
*
* Normally `llhttp` would error when `Transfer-Encoding` is present in
* conjunction with `Content-Length`. This error is important to prevent HTTP
* request smuggling, but may be less desirable for small number of cases
* involving legacy servers.
*
* **Enabling this flag can pose a security issue since you will be exposed to
* request smuggling attacks. USE WITH CAUTION!**
*/
LLHTTP_EXPORT
void llhttp_set_lenient_chunked_length(llhttp_t* parser, int enabled);
/* Enables/disables lenient handling of `Connection: close` and HTTP/1.0
* requests responses.
*
* Normally `llhttp` would error on (in strict mode) or discard (in loose mode)
* the HTTP request/response after the request/response with `Connection: close`
* and `Content-Length`. This is important to prevent cache poisoning attacks,
* but might interact badly with outdated and insecure clients. With this flag
* the extra request/response will be parsed normally.
*
* **Enabling this flag can pose a security issue since you will be exposed to
* poisoning attacks. USE WITH CAUTION!**
*/
LLHTTP_EXPORT
void llhttp_set_lenient_keep_alive(llhttp_t* parser, int enabled);
/* Enables/disables lenient handling of `Transfer-Encoding` header.
*
* Normally `llhttp` would error when a `Transfer-Encoding` has `chunked` value
* and another value after it (either in a single header or in multiple
* headers whose value are internally joined using `, `).
* This is mandated by the spec to reliably determine request body size and thus
* avoid request smuggling.
* With this flag the extra value will be parsed normally.
*
* **Enabling this flag can pose a security issue since you will be exposed to
* request smuggling attacks. USE WITH CAUTION!**
*/
LLHTTP_EXPORT
void llhttp_set_lenient_transfer_encoding(llhttp_t* parser, int enabled);
/* Enables/disables lenient handling of HTTP version.
*
* Normally `llhttp` would error when the HTTP version in the request or status line
* is not `0.9`, `1.0`, `1.1` or `2.0`.
* With this flag the invalid value will be parsed normally.
*
* **Enabling this flag can pose a security issue since you will allow unsupported
* HTTP versions. USE WITH CAUTION!**
*/
LLHTTP_EXPORT
void llhttp_set_lenient_version(llhttp_t* parser, int enabled);
/* Enables/disables lenient handling of additional data received after a message ends
* and keep-alive is disabled.
*
* Normally `llhttp` would error when additional unexpected data is received if the message
* contains the `Connection` header with `close` value.
* With this flag the extra data will discarded without throwing an error.
*
* **Enabling this flag can pose a security issue since you will be exposed to
* poisoning attacks. USE WITH CAUTION!**
*/
LLHTTP_EXPORT
void llhttp_set_lenient_data_after_close(llhttp_t* parser, int enabled);
/* Enables/disables lenient handling of incomplete CRLF sequences.
*
* Normally `llhttp` would error when a CR is not followed by LF when terminating the
* request line, the status line, the headers or a chunk header.
* With this flag only a CR is required to terminate such sections.
*
* **Enabling this flag can pose a security issue since you will be exposed to
* request smuggling attacks. USE WITH CAUTION!**
*/
LLHTTP_EXPORT
void llhttp_set_lenient_optional_lf_after_cr(llhttp_t* parser, int enabled);
/*
* Enables/disables lenient handling of line separators.
*
* Normally `llhttp` would error when a LF is not preceded by CR when terminating the
* request line, the status line, the headers, a chunk header or a chunk data.
* With this flag only a LF is required to terminate such sections.
*
* **Enabling this flag can pose a security issue since you will be exposed to
* request smuggling attacks. USE WITH CAUTION!**
*/
LLHTTP_EXPORT
void llhttp_set_lenient_optional_cr_before_lf(llhttp_t* parser, int enabled);
/* Enables/disables lenient handling of chunks not separated via CRLF.
*
* Normally `llhttp` would error when after a chunk data a CRLF is missing before
* starting a new chunk.
* With this flag the new chunk can start immediately after the previous one.
*
* **Enabling this flag can pose a security issue since you will be exposed to
* request smuggling attacks. USE WITH CAUTION!**
*/
LLHTTP_EXPORT
void llhttp_set_lenient_optional_crlf_after_chunk(llhttp_t* parser, int enabled);
/* Enables/disables lenient handling of spaces after chunk size.
*
* Normally `llhttp` would error when after a chunk size is followed by one or more
* spaces are present instead of a CRLF or `;`.
* With this flag this check is disabled.
*
* **Enabling this flag can pose a security issue since you will be exposed to
* request smuggling attacks. USE WITH CAUTION!**
*/
LLHTTP_EXPORT
void llhttp_set_lenient_spaces_after_chunk_size(llhttp_t* parser, int enabled);
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* INCLUDE_LLHTTP_API_H_ */
#endif /* INCLUDE_LLHTTP_H_ */

520
common/http/llhttp_api.c Normal file
View File

@@ -0,0 +1,520 @@
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "llhttp.h"
#ifdef __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wconversion"
#pragma GCC diagnostic ignored "-Wsign-conversion"
#endif
#define CALLBACK_MAYBE(PARSER, NAME) \
do { \
const llhttp_settings_t* settings; \
settings = (const llhttp_settings_t*) (PARSER)->settings; \
if (settings == NULL || settings->NAME == NULL) { \
err = 0; \
break; \
} \
err = settings->NAME((PARSER)); \
} while (0)
#define SPAN_CALLBACK_MAYBE(PARSER, NAME, START, LEN) \
do { \
const llhttp_settings_t* settings; \
settings = (const llhttp_settings_t*) (PARSER)->settings; \
if (settings == NULL || settings->NAME == NULL) { \
err = 0; \
break; \
} \
err = settings->NAME((PARSER), (START), (LEN)); \
if (err == -1) { \
err = HPE_USER; \
llhttp_set_error_reason((PARSER), "Span callback error in " #NAME); \
} \
} while (0)
void llhttp_init(llhttp_t* parser, llhttp_type_t type,
const llhttp_settings_t* settings) {
llhttp__internal_init(parser);
parser->type = type;
parser->settings = (void*) settings;
}
#if defined(__wasm__)
extern int wasm_on_message_begin(llhttp_t * p);
extern int wasm_on_url(llhttp_t* p, const char* at, size_t length);
extern int wasm_on_status(llhttp_t* p, const char* at, size_t length);
extern int wasm_on_header_field(llhttp_t* p, const char* at, size_t length);
extern int wasm_on_header_value(llhttp_t* p, const char* at, size_t length);
extern int wasm_on_headers_complete(llhttp_t * p, int status_code,
uint8_t upgrade, int should_keep_alive);
extern int wasm_on_body(llhttp_t* p, const char* at, size_t length);
extern int wasm_on_message_complete(llhttp_t * p);
static int wasm_on_headers_complete_wrap(llhttp_t* p) {
return wasm_on_headers_complete(p, p->status_code, p->upgrade,
llhttp_should_keep_alive(p));
}
const llhttp_settings_t wasm_settings = {
wasm_on_message_begin,
wasm_on_url,
wasm_on_status,
NULL,
NULL,
wasm_on_header_field,
wasm_on_header_value,
NULL,
NULL,
wasm_on_headers_complete_wrap,
wasm_on_body,
wasm_on_message_complete,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
};
llhttp_t* llhttp_alloc(llhttp_type_t type) {
llhttp_t* parser = malloc(sizeof(llhttp_t));
llhttp_init(parser, type, &wasm_settings);
return parser;
}
void llhttp_free(llhttp_t* parser) {
free(parser);
}
#endif // defined(__wasm__)
/* Some getters required to get stuff from the parser */
uint8_t llhttp_get_type(llhttp_t* parser) {
return parser->type;
}
uint8_t llhttp_get_http_major(llhttp_t* parser) {
return parser->http_major;
}
uint8_t llhttp_get_http_minor(llhttp_t* parser) {
return parser->http_minor;
}
uint8_t llhttp_get_method(llhttp_t* parser) {
return parser->method;
}
int llhttp_get_status_code(llhttp_t* parser) {
return parser->status_code;
}
uint8_t llhttp_get_upgrade(llhttp_t* parser) {
return parser->upgrade;
}
void llhttp_reset(llhttp_t* parser) {
llhttp_type_t type = parser->type;
const llhttp_settings_t* settings = parser->settings;
void* data = parser->data;
uint16_t lenient_flags = parser->lenient_flags;
llhttp__internal_init(parser);
parser->type = type;
parser->settings = (void*) settings;
parser->data = data;
parser->lenient_flags = lenient_flags;
}
llhttp_errno_t llhttp_execute(llhttp_t* parser, const char* data, size_t len) {
return llhttp__internal_execute(parser, data, data + len);
}
void llhttp_settings_init(llhttp_settings_t* settings) {
memset(settings, 0, sizeof(*settings));
}
llhttp_errno_t llhttp_finish(llhttp_t* parser) {
int err;
/* We're in an error state. Don't bother doing anything. */
if (parser->error != 0) {
return 0;
}
switch (parser->finish) {
case HTTP_FINISH_SAFE_WITH_CB:
CALLBACK_MAYBE(parser, on_message_complete);
if (err != HPE_OK) return err;
/* FALLTHROUGH */
case HTTP_FINISH_SAFE:
return HPE_OK;
case HTTP_FINISH_UNSAFE:
parser->reason = "Invalid EOF state";
return HPE_INVALID_EOF_STATE;
default:
abort();
}
}
void llhttp_pause(llhttp_t* parser) {
if (parser->error != HPE_OK) {
return;
}
parser->error = HPE_PAUSED;
parser->reason = "Paused";
}
void llhttp_resume(llhttp_t* parser) {
if (parser->error != HPE_PAUSED) {
return;
}
parser->error = 0;
}
void llhttp_resume_after_upgrade(llhttp_t* parser) {
if (parser->error != HPE_PAUSED_UPGRADE) {
return;
}
parser->error = 0;
}
llhttp_errno_t llhttp_get_errno(const llhttp_t* parser) {
return parser->error;
}
const char* llhttp_get_error_reason(const llhttp_t* parser) {
return parser->reason;
}
void llhttp_set_error_reason(llhttp_t* parser, const char* reason) {
parser->reason = reason;
}
const char* llhttp_get_error_pos(const llhttp_t* parser) {
return parser->error_pos;
}
const char* llhttp_errno_name(llhttp_errno_t err) {
#define HTTP_ERRNO_GEN(CODE, NAME, _) case HPE_##NAME: return "HPE_" #NAME;
switch (err) {
HTTP_ERRNO_MAP(HTTP_ERRNO_GEN)
default: abort();
}
#undef HTTP_ERRNO_GEN
}
const char* llhttp_method_name(llhttp_method_t method) {
#define HTTP_METHOD_GEN(NUM, NAME, STRING) case HTTP_##NAME: return #STRING;
switch (method) {
HTTP_ALL_METHOD_MAP(HTTP_METHOD_GEN)
default: abort();
}
#undef HTTP_METHOD_GEN
}
const char* llhttp_status_name(llhttp_status_t status) {
#define HTTP_STATUS_GEN(NUM, NAME, STRING) case HTTP_STATUS_##NAME: return #STRING;
switch (status) {
HTTP_STATUS_MAP(HTTP_STATUS_GEN)
default: abort();
}
#undef HTTP_STATUS_GEN
}
void llhttp_set_lenient_headers(llhttp_t* parser, int enabled) {
if (enabled) {
parser->lenient_flags |= LENIENT_HEADERS;
} else {
parser->lenient_flags &= ~LENIENT_HEADERS;
}
}
void llhttp_set_lenient_chunked_length(llhttp_t* parser, int enabled) {
if (enabled) {
parser->lenient_flags |= LENIENT_CHUNKED_LENGTH;
} else {
parser->lenient_flags &= ~LENIENT_CHUNKED_LENGTH;
}
}
void llhttp_set_lenient_keep_alive(llhttp_t* parser, int enabled) {
if (enabled) {
parser->lenient_flags |= LENIENT_KEEP_ALIVE;
} else {
parser->lenient_flags &= ~LENIENT_KEEP_ALIVE;
}
}
void llhttp_set_lenient_transfer_encoding(llhttp_t* parser, int enabled) {
if (enabled) {
parser->lenient_flags |= LENIENT_TRANSFER_ENCODING;
} else {
parser->lenient_flags &= ~LENIENT_TRANSFER_ENCODING;
}
}
void llhttp_set_lenient_version(llhttp_t* parser, int enabled) {
if (enabled) {
parser->lenient_flags |= LENIENT_VERSION;
} else {
parser->lenient_flags &= ~LENIENT_VERSION;
}
}
void llhttp_set_lenient_data_after_close(llhttp_t* parser, int enabled) {
if (enabled) {
parser->lenient_flags |= LENIENT_DATA_AFTER_CLOSE;
} else {
parser->lenient_flags &= ~LENIENT_DATA_AFTER_CLOSE;
}
}
void llhttp_set_lenient_optional_lf_after_cr(llhttp_t* parser, int enabled) {
if (enabled) {
parser->lenient_flags |= LENIENT_OPTIONAL_LF_AFTER_CR;
} else {
parser->lenient_flags &= ~LENIENT_OPTIONAL_LF_AFTER_CR;
}
}
void llhttp_set_lenient_optional_crlf_after_chunk(llhttp_t* parser, int enabled) {
if (enabled) {
parser->lenient_flags |= LENIENT_OPTIONAL_CRLF_AFTER_CHUNK;
} else {
parser->lenient_flags &= ~LENIENT_OPTIONAL_CRLF_AFTER_CHUNK;
}
}
void llhttp_set_lenient_optional_cr_before_lf(llhttp_t* parser, int enabled) {
if (enabled) {
parser->lenient_flags |= LENIENT_OPTIONAL_CR_BEFORE_LF;
} else {
parser->lenient_flags &= ~LENIENT_OPTIONAL_CR_BEFORE_LF;
}
}
void llhttp_set_lenient_spaces_after_chunk_size(llhttp_t* parser, int enabled) {
if (enabled) {
parser->lenient_flags |= LENIENT_SPACES_AFTER_CHUNK_SIZE;
} else {
parser->lenient_flags &= ~LENIENT_SPACES_AFTER_CHUNK_SIZE;
}
}
/* Callbacks */
int llhttp__on_message_begin(llhttp_t* s, const char* p, const char* endp) {
int err;
CALLBACK_MAYBE(s, on_message_begin);
return err;
}
int llhttp__on_url(llhttp_t* s, const char* p, const char* endp) {
int err;
SPAN_CALLBACK_MAYBE(s, on_url, p, endp - p);
return err;
}
int llhttp__on_url_complete(llhttp_t* s, const char* p, const char* endp) {
int err;
CALLBACK_MAYBE(s, on_url_complete);
return err;
}
int llhttp__on_status(llhttp_t* s, const char* p, const char* endp) {
int err;
SPAN_CALLBACK_MAYBE(s, on_status, p, endp - p);
return err;
}
int llhttp__on_status_complete(llhttp_t* s, const char* p, const char* endp) {
int err;
CALLBACK_MAYBE(s, on_status_complete);
return err;
}
int llhttp__on_method(llhttp_t* s, const char* p, const char* endp) {
int err;
SPAN_CALLBACK_MAYBE(s, on_method, p, endp - p);
return err;
}
int llhttp__on_method_complete(llhttp_t* s, const char* p, const char* endp) {
int err;
CALLBACK_MAYBE(s, on_method_complete);
return err;
}
int llhttp__on_version(llhttp_t* s, const char* p, const char* endp) {
int err;
SPAN_CALLBACK_MAYBE(s, on_version, p, endp - p);
return err;
}
int llhttp__on_version_complete(llhttp_t* s, const char* p, const char* endp) {
int err;
CALLBACK_MAYBE(s, on_version_complete);
return err;
}
int llhttp__on_header_field(llhttp_t* s, const char* p, const char* endp) {
int err;
SPAN_CALLBACK_MAYBE(s, on_header_field, p, endp - p);
return err;
}
int llhttp__on_header_field_complete(llhttp_t* s, const char* p, const char* endp) {
int err;
CALLBACK_MAYBE(s, on_header_field_complete);
return err;
}
int llhttp__on_header_value(llhttp_t* s, const char* p, const char* endp) {
int err;
SPAN_CALLBACK_MAYBE(s, on_header_value, p, endp - p);
return err;
}
int llhttp__on_header_value_complete(llhttp_t* s, const char* p, const char* endp) {
int err;
CALLBACK_MAYBE(s, on_header_value_complete);
return err;
}
int llhttp__on_headers_complete(llhttp_t* s, const char* p, const char* endp) {
int err;
CALLBACK_MAYBE(s, on_headers_complete);
return err;
}
int llhttp__on_message_complete(llhttp_t* s, const char* p, const char* endp) {
int err;
CALLBACK_MAYBE(s, on_message_complete);
return err;
}
int llhttp__on_body(llhttp_t* s, const char* p, const char* endp) {
int err;
SPAN_CALLBACK_MAYBE(s, on_body, p, endp - p);
return err;
}
int llhttp__on_chunk_header(llhttp_t* s, const char* p, const char* endp) {
int err;
CALLBACK_MAYBE(s, on_chunk_header);
return err;
}
int llhttp__on_chunk_extension_name(llhttp_t* s, const char* p, const char* endp) {
int err;
SPAN_CALLBACK_MAYBE(s, on_chunk_extension_name, p, endp - p);
return err;
}
int llhttp__on_chunk_extension_name_complete(llhttp_t* s, const char* p, const char* endp) {
int err;
CALLBACK_MAYBE(s, on_chunk_extension_name_complete);
return err;
}
int llhttp__on_chunk_extension_value(llhttp_t* s, const char* p, const char* endp) {
int err;
SPAN_CALLBACK_MAYBE(s, on_chunk_extension_value, p, endp - p);
return err;
}
int llhttp__on_chunk_extension_value_complete(llhttp_t* s, const char* p, const char* endp) {
int err;
CALLBACK_MAYBE(s, on_chunk_extension_value_complete);
return err;
}
int llhttp__on_chunk_complete(llhttp_t* s, const char* p, const char* endp) {
int err;
CALLBACK_MAYBE(s, on_chunk_complete);
return err;
}
int llhttp__on_reset(llhttp_t* s, const char* p, const char* endp) {
int err;
CALLBACK_MAYBE(s, on_reset);
return err;
}
/* Private */
void llhttp__debug(llhttp_t* s, const char* p, const char* endp,
const char* msg) {
if (p == endp) {
fprintf(stderr, "p=%p type=%d flags=%02x next=null debug=%s\n", s, s->type,
s->flags, msg);
} else {
fprintf(stderr, "p=%p type=%d flags=%02x next=%02x debug=%s\n", s,
s->type, s->flags, *p, msg);
}
}
#ifdef __GNUC__
#pragma GCC diagnostic pop
#endif

10180
common/http/llhttp_internal.c Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,170 @@
#include <stdio.h>
#ifndef LLHTTP__TEST
# include "llhttp.h"
#else
# define llhttp_t llparse_t
#endif /* */
int llhttp_message_needs_eof(const llhttp_t* parser);
int llhttp_should_keep_alive(const llhttp_t* parser);
int llhttp__before_headers_complete(llhttp_t* parser, const char* p,
const char* endp) {
/* Set this here so that on_headers_complete() callbacks can see it */
if ((parser->flags & F_UPGRADE) &&
(parser->flags & F_CONNECTION_UPGRADE)) {
/* For responses, "Upgrade: foo" and "Connection: upgrade" are
* mandatory only when it is a 101 Switching Protocols response,
* otherwise it is purely informational, to announce support.
*/
parser->upgrade =
(parser->type == HTTP_REQUEST || parser->status_code == 101);
} else {
parser->upgrade = (parser->method == HTTP_CONNECT);
}
return 0;
}
/* Return values:
* 0 - No body, `restart`, message_complete
* 1 - CONNECT request, `restart`, message_complete, and pause
* 2 - chunk_size_start
* 3 - body_identity
* 4 - body_identity_eof
* 5 - invalid transfer-encoding for request
*/
int llhttp__after_headers_complete(llhttp_t* parser, const char* p,
const char* endp) {
int hasBody;
hasBody = parser->flags & F_CHUNKED || parser->content_length > 0;
if (
(parser->upgrade && (parser->method == HTTP_CONNECT ||
(parser->flags & F_SKIPBODY) || !hasBody)) ||
/* See RFC 2616 section 4.4 - 1xx e.g. Continue */
(parser->type == HTTP_RESPONSE && parser->status_code == 101)
) {
/* Exit, the rest of the message is in a different protocol. */
return 1;
}
if (parser->type == HTTP_RESPONSE && parser->status_code == 100) {
/* No body, restart as the message is complete */
return 0;
}
/* See RFC 2616 section 4.4 */
if (
parser->flags & F_SKIPBODY || /* response to a HEAD request */
(
parser->type == HTTP_RESPONSE && (
parser->status_code == 102 || /* Processing */
parser->status_code == 103 || /* Early Hints */
parser->status_code == 204 || /* No Content */
parser->status_code == 304 /* Not Modified */
)
)
) {
return 0;
} else if (parser->flags & F_CHUNKED) {
/* chunked encoding - ignore Content-Length header, prepare for a chunk */
return 2;
} else if (parser->flags & F_TRANSFER_ENCODING) {
if (parser->type == HTTP_REQUEST &&
(parser->lenient_flags & LENIENT_CHUNKED_LENGTH) == 0 &&
(parser->lenient_flags & LENIENT_TRANSFER_ENCODING) == 0) {
/* RFC 7230 3.3.3 */
/* If a Transfer-Encoding header field
* is present in a request and the chunked transfer coding is not
* the final encoding, the message body length cannot be determined
* reliably; the server MUST respond with the 400 (Bad Request)
* status code and then close the connection.
*/
return 5;
} else {
/* RFC 7230 3.3.3 */
/* If a Transfer-Encoding header field is present in a response and
* the chunked transfer coding is not the final encoding, the
* message body length is determined by reading the connection until
* it is closed by the server.
*/
return 4;
}
} else {
if (!(parser->flags & F_CONTENT_LENGTH)) {
if (!llhttp_message_needs_eof(parser)) {
/* Assume content-length 0 - read the next */
return 0;
} else {
/* Read body until EOF */
return 4;
}
} else if (parser->content_length == 0) {
/* Content-Length header given but zero: Content-Length: 0\r\n */
return 0;
} else {
/* Content-Length header given and non-zero */
return 3;
}
}
}
int llhttp__after_message_complete(llhttp_t* parser, const char* p,
const char* endp) {
int should_keep_alive;
should_keep_alive = llhttp_should_keep_alive(parser);
parser->finish = HTTP_FINISH_SAFE;
parser->flags = 0;
/* NOTE: this is ignored in loose parsing mode */
return should_keep_alive;
}
int llhttp_message_needs_eof(const llhttp_t* parser) {
if (parser->type == HTTP_REQUEST) {
return 0;
}
/* See RFC 2616 section 4.4 */
if (parser->status_code / 100 == 1 || /* 1xx e.g. Continue */
parser->status_code == 204 || /* No Content */
parser->status_code == 304 || /* Not Modified */
(parser->flags & F_SKIPBODY)) { /* response to a HEAD request */
return 0;
}
/* RFC 7230 3.3.3, see `llhttp__after_headers_complete` */
if ((parser->flags & F_TRANSFER_ENCODING) &&
(parser->flags & F_CHUNKED) == 0) {
return 1;
}
if (parser->flags & (F_CHUNKED | F_CONTENT_LENGTH)) {
return 0;
}
return 1;
}
int llhttp_should_keep_alive(const llhttp_t* parser) {
if (parser->http_major > 0 && parser->http_minor > 0) {
/* HTTP/1.1 */
if (parser->flags & F_CONNECTION_CLOSE) {
return 0;
}
} else {
/* HTTP/1.0 or earlier */
if (!(parser->flags & F_CONNECTION_KEEP_ALIVE)) {
return 0;
}
}
return !llhttp_message_needs_eof(parser);
}

640
common/http/llhttp_url.c Normal file
View File

@@ -0,0 +1,640 @@
#include <assert.h>
#include <string.h>
#include "llhttp_url.h"
#ifdef __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wconversion"
#pragma GCC diagnostic ignored "-Wsign-conversion"
#endif
#ifndef BIT_AT
# define BIT_AT(a, i) \
(!!((unsigned int) (a)[(unsigned int) (i) >> 3] & \
(1 << ((unsigned int) (i) & 7))))
#endif
#if LLHTTP_STRICT_MODE
# define T(v) 0
#else
# define T(v) v
#endif
static const uint8_t normal_url_char[32] = {
/* 0 nul 1 soh 2 stx 3 etx 4 eot 5 enq 6 ack 7 bel */
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0,
/* 8 bs 9 ht 10 nl 11 vt 12 np 13 cr 14 so 15 si */
0 | T(2) | 0 | 0 | T(16) | 0 | 0 | 0,
/* 16 dle 17 dc1 18 dc2 19 dc3 20 dc4 21 nak 22 syn 23 etb */
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0,
/* 24 can 25 em 26 sub 27 esc 28 fs 29 gs 30 rs 31 us */
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0,
/* 32 sp 33 ! 34 " 35 # 36 $ 37 % 38 & 39 ' */
0 | 2 | 4 | 0 | 16 | 32 | 64 | 128,
/* 40 ( 41 ) 42 * 43 + 44 , 45 - 46 . 47 / */
1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
/* 48 0 49 1 50 2 51 3 52 4 53 5 54 6 55 7 */
1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
/* 56 8 57 9 58 : 59 ; 60 < 61 = 62 > 63 ? */
1 | 2 | 4 | 8 | 16 | 32 | 64 | 0,
/* 64 @ 65 A 66 B 67 C 68 D 69 E 70 F 71 G */
1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
/* 72 H 73 I 74 J 75 K 76 L 77 M 78 N 79 O */
1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
/* 80 P 81 Q 82 R 83 S 84 T 85 U 86 V 87 W */
1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
/* 88 X 89 Y 90 Z 91 [ 92 \ 93 ] 94 ^ 95 _ */
1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
/* 96 ` 97 a 98 b 99 c 100 d 101 e 102 f 103 g */
1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
/* 104 h 105 i 106 j 107 k 108 l 109 m 110 n 111 o */
1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
/* 112 p 113 q 114 r 115 s 116 t 117 u 118 v 119 w */
1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
/* 120 x 121 y 122 z 123 { 124 | 125 } 126 ~ 127 del */
1 | 2 | 4 | 8 | 16 | 32 | 64 | 0, };
#undef T
enum state
{ s_dead = 1 /* important that this is > 0 */
, s_start_req_or_res
, s_res_or_resp_H
, s_start_res
, s_res_H
, s_res_HT
, s_res_HTT
, s_res_HTTP
, s_res_http_major
, s_res_http_dot
, s_res_http_minor
, s_res_http_end
, s_res_first_status_code
, s_res_status_code
, s_res_status_start
, s_res_status
, s_res_line_almost_done
, s_start_req
, s_req_method
, s_req_spaces_before_url
, s_req_schema
, s_req_schema_slash
, s_req_schema_slash_slash
, s_req_server_start
, s_req_server
, s_req_server_with_at
, s_req_path
, s_req_query_string_start
, s_req_query_string
, s_req_fragment_start
, s_req_fragment
, s_req_http_start
, s_req_http_H
, s_req_http_HT
, s_req_http_HTT
, s_req_http_HTTP
, s_req_http_I
, s_req_http_IC
, s_req_http_major
, s_req_http_dot
, s_req_http_minor
, s_req_http_end
, s_req_line_almost_done
, s_header_field_start
, s_header_field
, s_header_value_discard_ws
, s_header_value_discard_ws_almost_done
, s_header_value_discard_lws
, s_header_value_start
, s_header_value
, s_header_value_lws
, s_header_almost_done
, s_chunk_size_start
, s_chunk_size
, s_chunk_parameters
, s_chunk_size_almost_done
, s_headers_almost_done
, s_headers_done
/* Important: 's_headers_done' must be the last 'header' state. All
* states beyond this must be 'body' states. It is used for overflow
* checking. See the PARSING_HEADER() macro.
*/
, s_chunk_data
, s_chunk_data_almost_done
, s_chunk_data_done
, s_body_identity
, s_body_identity_eof
, s_message_done
};
enum http_host_state
{
s_http_host_dead = 1
, s_http_userinfo_start
, s_http_userinfo
, s_http_host_start
, s_http_host_v6_start
, s_http_host
, s_http_host_v6
, s_http_host_v6_end
, s_http_host_v6_zone_start
, s_http_host_v6_zone
, s_http_host_port_start
, s_http_host_port
};
/* Macros for character classes; depends on strict-mode */
#define CR '\r'
#define LF '\n'
#define LOWER(c) (unsigned char)(c | 0x20)
#define IS_ALPHA(c) (LOWER(c) >= 'a' && LOWER(c) <= 'z')
#define IS_NUM(c) ((c) >= '0' && (c) <= '9')
#define IS_ALPHANUM(c) (IS_ALPHA(c) || IS_NUM(c))
#define IS_HEX(c) (IS_NUM(c) || (LOWER(c) >= 'a' && LOWER(c) <= 'f'))
#define IS_MARK(c) ((c) == '-' || (c) == '_' || (c) == '.' || \
(c) == '!' || (c) == '~' || (c) == '*' || (c) == '\'' || (c) == '(' || \
(c) == ')')
#define IS_USERINFO_CHAR(c) (IS_ALPHANUM(c) || IS_MARK(c) || (c) == '%' || \
(c) == ';' || (c) == ':' || (c) == '&' || (c) == '=' || (c) == '+' || \
(c) == '$' || (c) == ',')
#define STRICT_TOKEN(c) ((c == ' ') ? 0 : tokens[(unsigned char)c])
#if LLHTTP_STRICT_MODE
#define TOKEN(c) STRICT_TOKEN(c)
#define IS_URL_CHAR(c) (BIT_AT(normal_url_char, (unsigned char)c))
#define IS_HOST_CHAR(c) (IS_ALPHANUM(c) || (c) == '.' || (c) == '-')
#else
#define TOKEN(c) tokens[(unsigned char)c]
#define IS_URL_CHAR(c) \
(BIT_AT(normal_url_char, (unsigned char)c) || ((c) & 0x80))
#define IS_HOST_CHAR(c) \
(IS_ALPHANUM(c) || (c) == '.' || (c) == '-' || (c) == '_')
#endif
/* Our URL parser.
*
* This is designed to be shared by http_parser_execute() for URL validation,
* hence it has a state transition + byte-for-byte interface. In addition, it
* is meant to be embedded in http_parser_parse_url(), which does the dirty
* work of turning state transitions URL components for its API.
*
* This function should only be invoked with non-space characters. It is
* assumed that the caller cares about (and can detect) the transition between
* URL and non-URL states by looking for these.
*/
static enum state
parse_url_char(enum state s, const char ch)
{
if (ch == ' ' || ch == '\r' || ch == '\n') {
return s_dead;
}
#if LLHTTP_STRICT_MODE
if (ch == '\t' || ch == '\f') {
return s_dead;
}
#endif
switch (s) {
case s_req_spaces_before_url:
/* Proxied requests are followed by scheme of an absolute URI (alpha).
* All methods except CONNECT are followed by '/' or '*'.
*/
if (ch == '/' || ch == '*') {
return s_req_path;
}
if (IS_ALPHA(ch)) {
return s_req_schema;
}
break;
case s_req_schema:
if (IS_ALPHA(ch)) {
return s;
}
if (ch == ':') {
return s_req_schema_slash;
}
break;
case s_req_schema_slash:
if (ch == '/') {
return s_req_schema_slash_slash;
}
break;
case s_req_schema_slash_slash:
if (ch == '/') {
return s_req_server_start;
}
break;
case s_req_server_with_at:
if (ch == '@') {
return s_dead;
}
/* fall through */
case s_req_server_start:
case s_req_server:
if (ch == '/') {
return s_req_path;
}
if (ch == '?') {
return s_req_query_string_start;
}
if (ch == '@') {
return s_req_server_with_at;
}
if (IS_USERINFO_CHAR(ch) || ch == '[' || ch == ']') {
return s_req_server;
}
break;
case s_req_path:
if (IS_URL_CHAR(ch)) {
return s;
}
switch (ch) {
case '?':
return s_req_query_string_start;
case '#':
return s_req_fragment_start;
}
break;
case s_req_query_string_start:
case s_req_query_string:
if (IS_URL_CHAR(ch)) {
return s_req_query_string;
}
switch (ch) {
case '?':
/* allow extra '?' in query string */
return s_req_query_string;
case '#':
return s_req_fragment_start;
}
break;
case s_req_fragment_start:
if (IS_URL_CHAR(ch)) {
return s_req_fragment;
}
switch (ch) {
case '?':
return s_req_fragment;
case '#':
return s;
}
break;
case s_req_fragment:
if (IS_URL_CHAR(ch)) {
return s;
}
switch (ch) {
case '?':
case '#':
return s;
}
break;
default:
break;
}
/* We should never fall out of the switch above unless there's an error */
return s_dead;
}
static enum http_host_state
http_parse_host_char(enum http_host_state s, const char ch) {
switch(s) {
case s_http_userinfo:
case s_http_userinfo_start:
if (ch == '@') {
return s_http_host_start;
}
if (IS_USERINFO_CHAR(ch)) {
return s_http_userinfo;
}
break;
case s_http_host_start:
if (ch == '[') {
return s_http_host_v6_start;
}
if (IS_HOST_CHAR(ch)) {
return s_http_host;
}
break;
case s_http_host:
if (IS_HOST_CHAR(ch)) {
return s_http_host;
}
/* fall through */
case s_http_host_v6_end:
if (ch == ':') {
return s_http_host_port_start;
}
break;
case s_http_host_v6:
if (ch == ']') {
return s_http_host_v6_end;
}
/* fall through */
case s_http_host_v6_start:
if (IS_HEX(ch) || ch == ':' || ch == '.') {
return s_http_host_v6;
}
if (s == s_http_host_v6 && ch == '%') {
return s_http_host_v6_zone_start;
}
break;
case s_http_host_v6_zone:
if (ch == ']') {
return s_http_host_v6_end;
}
/* fall through */
case s_http_host_v6_zone_start:
/* RFC 6874 Zone ID consists of 1*( unreserved / pct-encoded) */
if (IS_ALPHANUM(ch) || ch == '%' || ch == '.' || ch == '-' || ch == '_' ||
ch == '~') {
return s_http_host_v6_zone;
}
break;
case s_http_host_port:
case s_http_host_port_start:
if (IS_NUM(ch)) {
return s_http_host_port;
}
break;
default:
break;
}
return s_http_host_dead;
}
static int
http_parse_host(const char * buf, struct http_parser_url *u, int found_at) {
enum http_host_state s;
const char *p;
size_t buflen = u->field_data[UF_HOST].off + u->field_data[UF_HOST].len;
assert(u->field_set & (1 << UF_HOST));
u->field_data[UF_HOST].len = 0;
s = found_at ? s_http_userinfo_start : s_http_host_start;
for (p = buf + u->field_data[UF_HOST].off; p < buf + buflen; p++) {
enum http_host_state new_s = http_parse_host_char(s, *p);
if (new_s == s_http_host_dead) {
return 1;
}
switch(new_s) {
case s_http_host:
if (s != s_http_host) {
u->field_data[UF_HOST].off = (uint16_t)(p - buf);
}
u->field_data[UF_HOST].len++;
break;
case s_http_host_v6:
if (s != s_http_host_v6) {
u->field_data[UF_HOST].off = (uint16_t)(p - buf);
}
u->field_data[UF_HOST].len++;
break;
case s_http_host_v6_zone_start:
case s_http_host_v6_zone:
u->field_data[UF_HOST].len++;
break;
case s_http_host_port:
if (s != s_http_host_port) {
u->field_data[UF_PORT].off = (uint16_t)(p - buf);
u->field_data[UF_PORT].len = 0;
u->field_set |= (1 << UF_PORT);
}
u->field_data[UF_PORT].len++;
break;
case s_http_userinfo:
if (s != s_http_userinfo) {
u->field_data[UF_USERINFO].off = (uint16_t)(p - buf);
u->field_data[UF_USERINFO].len = 0;
u->field_set |= (1 << UF_USERINFO);
}
u->field_data[UF_USERINFO].len++;
break;
default:
break;
}
s = new_s;
}
/* Make sure we don't end somewhere unexpected */
switch (s) {
case s_http_host_start:
case s_http_host_v6_start:
case s_http_host_v6:
case s_http_host_v6_zone_start:
case s_http_host_v6_zone:
case s_http_host_port_start:
case s_http_userinfo:
case s_http_userinfo_start:
return 1;
default:
break;
}
return 0;
}
void
http_parser_url_init(struct http_parser_url *u) {
memset(u, 0, sizeof(*u));
}
int
http_parser_parse_url(const char *buf, size_t buflen, int is_connect,
struct http_parser_url *u)
{
enum state s;
const char *p;
enum http_parser_url_fields uf, old_uf;
int found_at = 0;
if (buflen == 0) {
return 1;
}
u->port = u->field_set = 0;
s = is_connect ? s_req_server_start : s_req_spaces_before_url;
old_uf = UF_MAX;
for (p = buf; p < buf + buflen; p++) {
s = parse_url_char(s, *p);
/* Figure out the next field that we're operating on */
switch (s) {
case s_dead:
return 1;
/* Skip delimeters */
case s_req_schema_slash:
case s_req_schema_slash_slash:
case s_req_server_start:
case s_req_query_string_start:
case s_req_fragment_start:
continue;
case s_req_schema:
uf = UF_SCHEMA;
break;
case s_req_server_with_at:
found_at = 1;
/* fall through */
case s_req_server:
uf = UF_HOST;
break;
case s_req_path:
uf = UF_PATH;
break;
case s_req_query_string:
uf = UF_QUERY;
break;
case s_req_fragment:
uf = UF_FRAGMENT;
break;
default:
assert(!"Unexpected state");
return 1;
}
/* Nothing's changed; soldier on */
if (uf == old_uf) {
u->field_data[uf].len++;
continue;
}
u->field_data[uf].off = (uint16_t)(p - buf);
u->field_data[uf].len = 1;
u->field_set |= (1 << uf);
old_uf = uf;
}
/* host must be present if there is a schema */
/* parsing http:///toto will fail */
if ((u->field_set & (1 << UF_SCHEMA)) &&
(u->field_set & (1 << UF_HOST)) == 0) {
return 1;
}
if (u->field_set & (1 << UF_HOST)) {
if (http_parse_host(buf, u, found_at) != 0) {
return 1;
}
}
/* CONNECT requests can only contain "hostname:port" */
if (is_connect && u->field_set != ((1 << UF_HOST)|(1 << UF_PORT))) {
return 1;
}
if (u->field_set & (1 << UF_PORT)) {
uint16_t off;
uint16_t len;
const char* p;
const char* end;
unsigned long v;
off = u->field_data[UF_PORT].off;
len = u->field_data[UF_PORT].len;
end = buf + off + len;
/* NOTE: The characters are already validated and are in the [0-9] range */
assert((size_t)(off + len) <= buflen && "Port number overflow");
v = 0;
for (p = buf + off; p < end; p++) {
v *= 10;
v += *p - '0';
/* Ports have a max value of 2^16 */
if (v > 0xffff) {
return 1;
}
}
u->port = (uint16_t) v;
}
return 0;
}
#ifdef __GNUC__
#pragma GCC diagnostic pop
#endif

60
common/http/llhttp_url.h Normal file
View File

@@ -0,0 +1,60 @@
#ifndef INCLUDE_LLHTTP_URL_H_
#define INCLUDE_LLHTTP_URL_H_
#ifdef __cplusplus
extern "C" {
#endif
#include <stddef.h>
#include <stdint.h>
// copy code from http_parser
/* Compile with -DLLHTTP_STRICT_MODE=0 to make less checks, but run
* faster
*/
#ifndef LLHTTP_STRICT_MODE
# define LLHTTP_STRICT_MODE 1
#endif
enum http_parser_url_fields
{ UF_SCHEMA = 0
, UF_HOST = 1
, UF_PORT = 2
, UF_PATH = 3
, UF_QUERY = 4
, UF_FRAGMENT = 5
, UF_USERINFO = 6
, UF_MAX = 7
};
/* Result structure for http_parser_parse_url().
*
* Callers should index into field_data[] with UF_* values iff field_set
* has the relevant (1 << UF_*) bit set. As a courtesy to clients (and
* because we probably have padding left over), we convert any port to
* a uint16_t.
*/
struct http_parser_url {
uint16_t field_set; /* Bitmask of (1 << UF_*) values */
uint16_t port; /* Converted UF_PORT string */
struct {
uint16_t off; /* Offset into buffer in which field starts */
uint16_t len; /* Length of run in buffer */
} field_data[UF_MAX];
};
/* Initialize all http_parser_url members to 0 */
void http_parser_url_init(struct http_parser_url *u);
/* Parse a URL; return nonzero on failure */
int http_parser_parse_url(const char *buf, size_t buflen,
int is_connect, struct http_parser_url *u);
#ifdef __cplusplus
}
#endif
#endif // INCLUDE_LLHTTP_URL_H_

4
common/kcp/Readme.txt Normal file
View File

@@ -0,0 +1,4 @@
Modifications
--------------------
1. kcp.c ignore warning: "-Wconversion", "-Wsign-conversion"
2. kcp.c ikcp_input(): "if (conv != kcp->conv) return -1;" -> "conv = kcp->conv;"

1307
common/kcp/ikcp.c Normal file

File diff suppressed because it is too large Load Diff

416
common/kcp/ikcp.h Normal file
View File

@@ -0,0 +1,416 @@
//=====================================================================
//
// KCP - A Better ARQ Protocol Implementation
// skywind3000 (at) gmail.com, 2010-2011
//
// Features:
// + Average RTT reduce 30% - 40% vs traditional ARQ like tcp.
// + Maximum RTT reduce three times vs tcp.
// + Lightweight, distributed as a single source file.
//
//=====================================================================
#ifndef __IKCP_H__
#define __IKCP_H__
#include <stddef.h>
#include <stdlib.h>
#include <assert.h>
//=====================================================================
// 32BIT INTEGER DEFINITION
//=====================================================================
#ifndef __INTEGER_32_BITS__
#define __INTEGER_32_BITS__
#if defined(_WIN64) || defined(WIN64) || defined(__amd64__) || \
defined(__x86_64) || defined(__x86_64__) || defined(_M_IA64) || \
defined(_M_AMD64)
typedef unsigned int ISTDUINT32;
typedef int ISTDINT32;
#elif defined(_WIN32) || defined(WIN32) || defined(__i386__) || \
defined(__i386) || defined(_M_X86)
typedef unsigned long ISTDUINT32;
typedef long ISTDINT32;
#elif defined(__MACOS__)
typedef UInt32 ISTDUINT32;
typedef SInt32 ISTDINT32;
#elif defined(__APPLE__) && defined(__MACH__)
#include <sys/types.h>
typedef u_int32_t ISTDUINT32;
typedef int32_t ISTDINT32;
#elif defined(__BEOS__)
#include <sys/inttypes.h>
typedef u_int32_t ISTDUINT32;
typedef int32_t ISTDINT32;
#elif (defined(_MSC_VER) || defined(__BORLANDC__)) && (!defined(__MSDOS__))
typedef unsigned __int32 ISTDUINT32;
typedef __int32 ISTDINT32;
#elif defined(__GNUC__)
#include <stdint.h>
typedef uint32_t ISTDUINT32;
typedef int32_t ISTDINT32;
#else
typedef unsigned long ISTDUINT32;
typedef long ISTDINT32;
#endif
#endif
//=====================================================================
// Integer Definition
//=====================================================================
#ifndef __IINT8_DEFINED
#define __IINT8_DEFINED
typedef char IINT8;
#endif
#ifndef __IUINT8_DEFINED
#define __IUINT8_DEFINED
typedef unsigned char IUINT8;
#endif
#ifndef __IUINT16_DEFINED
#define __IUINT16_DEFINED
typedef unsigned short IUINT16;
#endif
#ifndef __IINT16_DEFINED
#define __IINT16_DEFINED
typedef short IINT16;
#endif
#ifndef __IINT32_DEFINED
#define __IINT32_DEFINED
typedef ISTDINT32 IINT32;
#endif
#ifndef __IUINT32_DEFINED
#define __IUINT32_DEFINED
typedef ISTDUINT32 IUINT32;
#endif
#ifndef __IINT64_DEFINED
#define __IINT64_DEFINED
#if defined(_MSC_VER) || defined(__BORLANDC__)
typedef __int64 IINT64;
#else
typedef long long IINT64;
#endif
#endif
#ifndef __IUINT64_DEFINED
#define __IUINT64_DEFINED
#if defined(_MSC_VER) || defined(__BORLANDC__)
typedef unsigned __int64 IUINT64;
#else
typedef unsigned long long IUINT64;
#endif
#endif
#ifndef INLINE
#if defined(__GNUC__)
#if (__GNUC__ > 3) || ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 1))
#define INLINE __inline__ __attribute__((always_inline))
#else
#define INLINE __inline__
#endif
#elif (defined(_MSC_VER) || defined(__BORLANDC__) || defined(__WATCOMC__))
#define INLINE __inline
#else
#define INLINE
#endif
#endif
#if (!defined(__cplusplus)) && (!defined(inline))
#define inline INLINE
#endif
//=====================================================================
// QUEUE DEFINITION
//=====================================================================
#ifndef __IQUEUE_DEF__
#define __IQUEUE_DEF__
struct IQUEUEHEAD {
struct IQUEUEHEAD *next, *prev;
};
typedef struct IQUEUEHEAD iqueue_head;
//---------------------------------------------------------------------
// queue init
//---------------------------------------------------------------------
#define IQUEUE_HEAD_INIT(name) { &(name), &(name) }
#define IQUEUE_HEAD(name) \
struct IQUEUEHEAD name = IQUEUE_HEAD_INIT(name)
#define IQUEUE_INIT(ptr) ( \
(ptr)->next = (ptr), (ptr)->prev = (ptr))
#define IOFFSETOF(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
#define ICONTAINEROF(ptr, type, member) ( \
(type*)( ((char*)((type*)ptr)) - IOFFSETOF(type, member)) )
#define IQUEUE_ENTRY(ptr, type, member) ICONTAINEROF(ptr, type, member)
//---------------------------------------------------------------------
// queue operation
//---------------------------------------------------------------------
#define IQUEUE_ADD(node, head) ( \
(node)->prev = (head), (node)->next = (head)->next, \
(head)->next->prev = (node), (head)->next = (node))
#define IQUEUE_ADD_TAIL(node, head) ( \
(node)->prev = (head)->prev, (node)->next = (head), \
(head)->prev->next = (node), (head)->prev = (node))
#define IQUEUE_DEL_BETWEEN(p, n) ((n)->prev = (p), (p)->next = (n))
#define IQUEUE_DEL(entry) (\
(entry)->next->prev = (entry)->prev, \
(entry)->prev->next = (entry)->next, \
(entry)->next = 0, (entry)->prev = 0)
#define IQUEUE_DEL_INIT(entry) do { \
IQUEUE_DEL(entry); IQUEUE_INIT(entry); } while (0)
#define IQUEUE_IS_EMPTY(entry) ((entry) == (entry)->next)
#define iqueue_init IQUEUE_INIT
#define iqueue_entry IQUEUE_ENTRY
#define iqueue_add IQUEUE_ADD
#define iqueue_add_tail IQUEUE_ADD_TAIL
#define iqueue_del IQUEUE_DEL
#define iqueue_del_init IQUEUE_DEL_INIT
#define iqueue_is_empty IQUEUE_IS_EMPTY
#define IQUEUE_FOREACH(iterator, head, TYPE, MEMBER) \
for ((iterator) = iqueue_entry((head)->next, TYPE, MEMBER); \
&((iterator)->MEMBER) != (head); \
(iterator) = iqueue_entry((iterator)->MEMBER.next, TYPE, MEMBER))
#define iqueue_foreach(iterator, head, TYPE, MEMBER) \
IQUEUE_FOREACH(iterator, head, TYPE, MEMBER)
#define iqueue_foreach_entry(pos, head) \
for( (pos) = (head)->next; (pos) != (head) ; (pos) = (pos)->next )
#define __iqueue_splice(list, head) do { \
iqueue_head *first = (list)->next, *last = (list)->prev; \
iqueue_head *at = (head)->next; \
(first)->prev = (head), (head)->next = (first); \
(last)->next = (at), (at)->prev = (last); } while (0)
#define iqueue_splice(list, head) do { \
if (!iqueue_is_empty(list)) __iqueue_splice(list, head); } while (0)
#define iqueue_splice_init(list, head) do { \
iqueue_splice(list, head); iqueue_init(list); } while (0)
#ifdef _MSC_VER
#pragma warning(disable:4311)
#pragma warning(disable:4312)
#pragma warning(disable:4996)
#endif
#endif
//---------------------------------------------------------------------
// BYTE ORDER & ALIGNMENT
//---------------------------------------------------------------------
#ifndef IWORDS_BIG_ENDIAN
#ifdef _BIG_ENDIAN_
#if _BIG_ENDIAN_
#define IWORDS_BIG_ENDIAN 1
#endif
#endif
#ifndef IWORDS_BIG_ENDIAN
#if defined(__hppa__) || \
defined(__m68k__) || defined(mc68000) || defined(_M_M68K) || \
(defined(__MIPS__) && defined(__MIPSEB__)) || \
defined(__ppc__) || defined(__POWERPC__) || defined(_M_PPC) || \
defined(__sparc__) || defined(__powerpc__) || \
defined(__mc68000__) || defined(__s390x__) || defined(__s390__)
#define IWORDS_BIG_ENDIAN 1
#endif
#endif
#ifndef IWORDS_BIG_ENDIAN
#define IWORDS_BIG_ENDIAN 0
#endif
#endif
#ifndef IWORDS_MUST_ALIGN
#if defined(__i386__) || defined(__i386) || defined(_i386_)
#define IWORDS_MUST_ALIGN 0
#elif defined(_M_IX86) || defined(_X86_) || defined(__x86_64__)
#define IWORDS_MUST_ALIGN 0
#elif defined(__amd64) || defined(__amd64__)
#define IWORDS_MUST_ALIGN 0
#else
#define IWORDS_MUST_ALIGN 1
#endif
#endif
//=====================================================================
// SEGMENT
//=====================================================================
struct IKCPSEG
{
struct IQUEUEHEAD node;
IUINT32 conv;
IUINT32 cmd;
IUINT32 frg;
IUINT32 wnd;
IUINT32 ts;
IUINT32 sn;
IUINT32 una;
IUINT32 len;
IUINT32 resendts;
IUINT32 rto;
IUINT32 fastack;
IUINT32 xmit;
char data[1];
};
//---------------------------------------------------------------------
// IKCPCB
//---------------------------------------------------------------------
struct IKCPCB
{
IUINT32 conv, mtu, mss, state;
IUINT32 snd_una, snd_nxt, rcv_nxt;
IUINT32 ts_recent, ts_lastack, ssthresh;
IINT32 rx_rttval, rx_srtt, rx_rto, rx_minrto;
IUINT32 snd_wnd, rcv_wnd, rmt_wnd, cwnd, probe;
IUINT32 current, interval, ts_flush, xmit;
IUINT32 nrcv_buf, nsnd_buf;
IUINT32 nrcv_que, nsnd_que;
IUINT32 nodelay, updated;
IUINT32 ts_probe, probe_wait;
IUINT32 dead_link, incr;
struct IQUEUEHEAD snd_queue;
struct IQUEUEHEAD rcv_queue;
struct IQUEUEHEAD snd_buf;
struct IQUEUEHEAD rcv_buf;
IUINT32 *acklist;
IUINT32 ackcount;
IUINT32 ackblock;
void *user;
char *buffer;
int fastresend;
int fastlimit;
int nocwnd, stream;
int logmask;
int (*output)(const char *buf, int len, struct IKCPCB *kcp, void *user);
void (*writelog)(const char *log, struct IKCPCB *kcp, void *user);
};
typedef struct IKCPCB ikcpcb;
#define IKCP_LOG_OUTPUT 1
#define IKCP_LOG_INPUT 2
#define IKCP_LOG_SEND 4
#define IKCP_LOG_RECV 8
#define IKCP_LOG_IN_DATA 16
#define IKCP_LOG_IN_ACK 32
#define IKCP_LOG_IN_PROBE 64
#define IKCP_LOG_IN_WINS 128
#define IKCP_LOG_OUT_DATA 256
#define IKCP_LOG_OUT_ACK 512
#define IKCP_LOG_OUT_PROBE 1024
#define IKCP_LOG_OUT_WINS 2048
#ifdef __cplusplus
extern "C" {
#endif
//---------------------------------------------------------------------
// interface
//---------------------------------------------------------------------
// create a new kcp control object, 'conv' must equal in two endpoint
// from the same connection. 'user' will be passed to the output callback
// output callback can be setup like this: 'kcp->output = my_udp_output'
ikcpcb* ikcp_create(IUINT32 conv, void *user);
// release kcp control object
void ikcp_release(ikcpcb *kcp);
// set output callback, which will be invoked by kcp
void ikcp_setoutput(ikcpcb *kcp, int (*output)(const char *buf, int len,
ikcpcb *kcp, void *user));
// user/upper level recv: returns size, returns below zero for EAGAIN
int ikcp_recv(ikcpcb *kcp, char *buffer, int len);
// user/upper level send, returns below zero for error
int ikcp_send(ikcpcb *kcp, const char *buffer, int len);
// update state (call it repeatedly, every 10ms-100ms), or you can ask
// ikcp_check when to call it again (without ikcp_input/_send calling).
// 'current' - current timestamp in millisec.
void ikcp_update(ikcpcb *kcp, IUINT32 current);
// Determine when should you invoke ikcp_update:
// returns when you should invoke ikcp_update in millisec, if there
// is no ikcp_input/_send calling. you can call ikcp_update in that
// time, instead of call update repeatly.
// Important to reduce unnacessary ikcp_update invoking. use it to
// schedule ikcp_update (eg. implementing an epoll-like mechanism,
// or optimize ikcp_update when handling massive kcp connections)
IUINT32 ikcp_check(const ikcpcb *kcp, IUINT32 current);
// when you received a low level packet (eg. UDP packet), call it
int ikcp_input(ikcpcb *kcp, const char *data, long size);
// flush pending data
void ikcp_flush(ikcpcb *kcp);
// check the size of next message in the recv queue
int ikcp_peeksize(const ikcpcb *kcp);
// change MTU size, default is 1400
int ikcp_setmtu(ikcpcb *kcp, int mtu);
// set maximum window size: sndwnd=32, rcvwnd=32 by default
int ikcp_wndsize(ikcpcb *kcp, int sndwnd, int rcvwnd);
// get how many packet is waiting to be sent
int ikcp_waitsnd(const ikcpcb *kcp);
// fastest: ikcp_nodelay(kcp, 1, 20, 2, 1)
// nodelay: 0:disable(default), 1:enable
// interval: internal update timer interval in millisec, default is 100ms
// resend: 0:disable fast resend(default), 1:enable fast resend
// nc: 0:normal congestion control(default), 1:disable congestion control
int ikcp_nodelay(ikcpcb *kcp, int nodelay, int interval, int resend, int nc);
void ikcp_log(ikcpcb *kcp, int mask, const char *fmt, ...);
// setup allocator
void ikcp_allocator(void* (*new_malloc)(size_t), void (*new_free)(void*));
// read conv
IUINT32 ikcp_getconv(const void *ptr);
#ifdef __cplusplus
}
#endif
#endif