BG
This commit is contained in:
298
common/BufferPool.cpp
Normal file
298
common/BufferPool.cpp
Normal 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
869
common/BufferPool.h
Normal 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
198
common/BufferPtr.h
Normal 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
291
common/CriSec.h
Normal 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
24
common/Event.cpp
Normal 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
530
common/Event.h
Normal 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
237
common/FileHelper.cpp
Normal 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
123
common/FileHelper.h
Normal 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
393
common/FuncHelper.cpp
Normal 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
426
common/FuncHelper.h
Normal 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
40
common/GeneralHelper.h
Normal 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
348
common/IODispatcher.cpp
Normal 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
272
common/IODispatcher.h
Normal 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
62
common/PollHelper.cpp
Normal 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
51
common/PollHelper.h
Normal 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
152
common/PrivateHeap.h
Normal 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
228
common/RWLock.cpp
Normal 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
128
common/RWLock.h
Normal 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
1731
common/RingBuffer.h
Normal file
File diff suppressed because it is too large
Load Diff
1068
common/STLHelper.h
Normal file
1068
common/STLHelper.h
Normal file
File diff suppressed because it is too large
Load Diff
117
common/Semaphore.h
Normal file
117
common/Semaphore.h
Normal 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
183
common/SignalHandler.h
Normal 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
117
common/Singleton.h
Normal 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
930
common/StringT.h
Normal 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
66
common/SysHelper.cpp
Normal 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
186
common/SysHelper.h
Normal 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
50
common/Thread.cpp
Normal 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
612
common/Thread.h
Normal 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
11
common/http/Readme.txt
Normal 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
903
common/http/llhttp.h
Normal 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
520
common/http/llhttp_api.c
Normal 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
10180
common/http/llhttp_internal.c
Normal file
File diff suppressed because it is too large
Load Diff
170
common/http/llhttp_support.c
Normal file
170
common/http/llhttp_support.c
Normal 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
640
common/http/llhttp_url.c
Normal 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
60
common/http/llhttp_url.h
Normal 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
4
common/kcp/Readme.txt
Normal 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
1307
common/kcp/ikcp.c
Normal file
File diff suppressed because it is too large
Load Diff
416
common/kcp/ikcp.h
Normal file
416
common/kcp/ikcp.h
Normal 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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user