Micro-optimize the InputMap class a little.
* Cuts the DefaultInputMap setup time. It was about a millisecond; now it's about a quarter of a millisecond.
This commit is contained in:
parent
6795a6ee49
commit
850eb9ff3f
@ -45,7 +45,7 @@ ConsoleInput::ConsoleInput(DsrSender *dsrSender) :
|
||||
{
|
||||
addDefaultEntriesToInputMap(m_inputMap);
|
||||
if (hasDebugFlag("dump_input_map")) {
|
||||
dumpInputMap(m_inputMap);
|
||||
m_inputMap.dumpInputMap();
|
||||
}
|
||||
}
|
||||
|
||||
@ -151,20 +151,19 @@ int ConsoleInput::scanKeyPress(std::vector<INPUT_RECORD> &records,
|
||||
}
|
||||
|
||||
// Search the input map.
|
||||
InputMap::Key match;
|
||||
bool incomplete;
|
||||
int matchLen;
|
||||
const InputMap::Key *match =
|
||||
lookupKey(input, inputSize, incomplete, matchLen);
|
||||
int matchLen = m_inputMap.lookupKey(input, inputSize, match, incomplete);
|
||||
if (!isEof && incomplete) {
|
||||
// Incomplete match -- need more characters (or wait for a
|
||||
// timeout to signify flushed input).
|
||||
trace("Incomplete escape sequence");
|
||||
return -1;
|
||||
} else if (match != NULL) {
|
||||
} else if (matchLen > 0) {
|
||||
appendKeyPress(records,
|
||||
match->virtualKey,
|
||||
match->unicodeChar,
|
||||
match->keyState);
|
||||
match.virtualKey,
|
||||
match.unicodeChar,
|
||||
match.keyState);
|
||||
return matchLen;
|
||||
}
|
||||
|
||||
@ -200,7 +199,7 @@ int ConsoleInput::scanKeyPress(std::vector<INPUT_RECORD> &records,
|
||||
void ConsoleInput::appendUtf8Char(std::vector<INPUT_RECORD> &records,
|
||||
const char *charBuffer,
|
||||
const int charLen,
|
||||
const int keyState)
|
||||
const uint16_t keyState)
|
||||
{
|
||||
WCHAR wideInput[2];
|
||||
int wideLen = MultiByteToWideChar(CP_UTF8,
|
||||
@ -211,8 +210,8 @@ void ConsoleInput::appendUtf8Char(std::vector<INPUT_RECORD> &records,
|
||||
sizeof(wideInput) / sizeof(wideInput[0]));
|
||||
for (int i = 0; i < wideLen; ++i) {
|
||||
short charScan = VkKeyScan(wideInput[i]);
|
||||
int virtualKey = 0;
|
||||
int charKeyState = keyState;
|
||||
uint16_t virtualKey = 0;
|
||||
uint16_t charKeyState = keyState;
|
||||
if (charScan != -1) {
|
||||
virtualKey = charScan & 0xFF;
|
||||
if (charScan & 0x100)
|
||||
@ -227,9 +226,9 @@ void ConsoleInput::appendUtf8Char(std::vector<INPUT_RECORD> &records,
|
||||
}
|
||||
|
||||
void ConsoleInput::appendKeyPress(std::vector<INPUT_RECORD> &records,
|
||||
int virtualKey,
|
||||
int unicodeChar,
|
||||
int keyState)
|
||||
uint16_t virtualKey,
|
||||
uint16_t unicodeChar,
|
||||
uint16_t keyState)
|
||||
{
|
||||
const bool ctrl = keyState & LEFT_CTRL_PRESSED;
|
||||
const bool alt = keyState & LEFT_ALT_PRESSED;
|
||||
@ -243,7 +242,7 @@ void ConsoleInput::appendKeyPress(std::vector<INPUT_RECORD> &records,
|
||||
}
|
||||
}
|
||||
|
||||
int stepKeyState = 0;
|
||||
uint16_t stepKeyState = 0;
|
||||
if (ctrl) {
|
||||
stepKeyState |= LEFT_CTRL_PRESSED;
|
||||
appendInputRecord(records, TRUE, VK_CONTROL, 0, stepKeyState);
|
||||
@ -284,9 +283,9 @@ void ConsoleInput::appendKeyPress(std::vector<INPUT_RECORD> &records,
|
||||
|
||||
void ConsoleInput::appendInputRecord(std::vector<INPUT_RECORD> &records,
|
||||
BOOL keyDown,
|
||||
int virtualKey,
|
||||
int unicodeChar,
|
||||
int keyState)
|
||||
uint16_t virtualKey,
|
||||
uint16_t unicodeChar,
|
||||
uint16_t keyState)
|
||||
{
|
||||
INPUT_RECORD ir;
|
||||
memset(&ir, 0, sizeof(ir));
|
||||
@ -324,35 +323,6 @@ int ConsoleInput::utf8CharLength(char firstByte)
|
||||
}
|
||||
}
|
||||
|
||||
// Find the longest matching key and node.
|
||||
const InputMap::Key *
|
||||
ConsoleInput::lookupKey(const char *input, int inputSize,
|
||||
bool &incompleteOut, int &matchLenOut)
|
||||
{
|
||||
incompleteOut = false;
|
||||
matchLenOut = 0;
|
||||
|
||||
InputMap *node = &m_inputMap;
|
||||
const InputMap::Key *longestMatch = NULL;
|
||||
int longestMatchLen = 0;
|
||||
|
||||
for (int i = 0; i < inputSize; ++i) {
|
||||
unsigned char ch = input[i];
|
||||
node = node->getChild(ch);
|
||||
//trace("ch: %d --> node:%p", ch, node);
|
||||
if (node == NULL) {
|
||||
matchLenOut = longestMatchLen;
|
||||
return longestMatch;
|
||||
} else if (node->getKey() != NULL) {
|
||||
longestMatchLen = i + 1;
|
||||
longestMatch = node->getKey();
|
||||
}
|
||||
}
|
||||
incompleteOut = node->hasChildren();
|
||||
matchLenOut = longestMatchLen;
|
||||
return longestMatch;
|
||||
}
|
||||
|
||||
// Match the Device Status Report console input: ESC [ nn ; mm R
|
||||
// Returns:
|
||||
// 0 no match
|
||||
|
@ -21,9 +21,11 @@
|
||||
#ifndef CONSOLEINPUT_H
|
||||
#define CONSOLEINPUT_H
|
||||
|
||||
#include <windows.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <windows.h>
|
||||
|
||||
#include "InputMap.h"
|
||||
|
||||
@ -47,19 +49,17 @@ private:
|
||||
void appendUtf8Char(std::vector<INPUT_RECORD> &records,
|
||||
const char *charBuffer,
|
||||
int charLen,
|
||||
int keyState);
|
||||
uint16_t keyState);
|
||||
void appendKeyPress(std::vector<INPUT_RECORD> &records,
|
||||
int virtualKey,
|
||||
int unicodeChar,
|
||||
int keyState);
|
||||
uint16_t virtualKey,
|
||||
uint16_t unicodeChar,
|
||||
uint16_t keyState);
|
||||
void appendInputRecord(std::vector<INPUT_RECORD> &records,
|
||||
BOOL keyDown,
|
||||
int virtualKey,
|
||||
int unicodeChar,
|
||||
int keyState);
|
||||
uint16_t virtualKey,
|
||||
uint16_t unicodeChar,
|
||||
uint16_t keyState);
|
||||
static int utf8CharLength(char firstByte);
|
||||
const InputMap::Key *lookupKey(const char *input, int inputSize,
|
||||
bool &incompleteOut, int &matchLenOut);
|
||||
static int matchDsr(const char *input, int inputSize);
|
||||
|
||||
private:
|
||||
|
@ -197,13 +197,13 @@ struct ExpandContext {
|
||||
};
|
||||
|
||||
static inline void setEncoding(const ExpandContext &ctx, char *end,
|
||||
int extraKeyState) {
|
||||
uint16_t extraKeyState) {
|
||||
InputMap::Key k = ctx.e.key;
|
||||
k.keyState |= extraKeyState;
|
||||
ctx.inputMap.set(ctx.buffer, end - ctx.buffer, k);
|
||||
}
|
||||
|
||||
static inline int keyStateForMod(int mod) {
|
||||
static inline uint16_t keyStateForMod(int mod) {
|
||||
int ret = 0;
|
||||
if ((mod - 1) & kCsiShiftModifier) ret |= SHIFT_PRESSED;
|
||||
if ((mod - 1) & kCsiAltModifier) ret |= LEFT_ALT_PRESSED;
|
||||
@ -212,7 +212,7 @@ static inline int keyStateForMod(int mod) {
|
||||
}
|
||||
|
||||
static void expandNumericEncodingSuffix(const ExpandContext &ctx, char *p,
|
||||
int extraKeyState) {
|
||||
uint16_t extraKeyState) {
|
||||
{
|
||||
char *q = p;
|
||||
*q++ = '~';
|
||||
@ -237,7 +237,7 @@ static void expandNumericEncodingSuffix(const ExpandContext &ctx, char *p,
|
||||
|
||||
template <bool is_numeric>
|
||||
static inline void expandEncodingAfterAltPrefix(
|
||||
const ExpandContext &ctx, char *p, int extraKeyState) {
|
||||
const ExpandContext &ctx, char *p, uint16_t extraKeyState) {
|
||||
*p++ = '\x1b';
|
||||
*p++ = ctx.e.prefix;
|
||||
if (ctx.e.modifiers & kBare) {
|
||||
|
@ -24,8 +24,10 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "SimplePool.h"
|
||||
#include "../shared/DebugClient.h"
|
||||
#include "../shared/UnixCtrlChars.h"
|
||||
#include "../shared/WinptyAssert.h"
|
||||
|
||||
namespace {
|
||||
|
||||
@ -105,34 +107,6 @@ static const char *getVirtualKeyString(int virtualKey)
|
||||
}
|
||||
}
|
||||
|
||||
static void dumpInputMapHelper(InputMap &inputMap, std::string &encoding) {
|
||||
if (inputMap.getKey() != NULL) {
|
||||
trace("%s -> %s",
|
||||
encoding.c_str(),
|
||||
inputMap.getKey()->toString().c_str());
|
||||
}
|
||||
for (int i = 0; i < 256; ++i) {
|
||||
InputMap *child = inputMap.getChild(i);
|
||||
if (child != NULL) {
|
||||
size_t oldSize = encoding.size();
|
||||
if (!encoding.empty()) {
|
||||
encoding.push_back(' ');
|
||||
}
|
||||
char ctrlChar = decodeUnixCtrlChar(i);
|
||||
if (ctrlChar != '\0') {
|
||||
encoding.push_back('^');
|
||||
encoding.push_back(static_cast<char>(ctrlChar));
|
||||
} else if (i == ' ') {
|
||||
encoding.append("' '");
|
||||
} else {
|
||||
encoding.push_back(static_cast<char>(i));
|
||||
}
|
||||
dumpInputMapHelper(*child, encoding);
|
||||
encoding.resize(oldSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
std::string InputMap::Key::toString() const {
|
||||
@ -166,44 +140,110 @@ std::string InputMap::Key::toString() const {
|
||||
return ret;
|
||||
}
|
||||
|
||||
InputMap::InputMap() : m_key(NULL), m_children(NULL) {
|
||||
void InputMap::set(const char *encoding, int encodingLen, const Key &key) {
|
||||
ASSERT(encodingLen > 0);
|
||||
setHelper(m_root, encoding, encodingLen, key);
|
||||
}
|
||||
|
||||
InputMap::~InputMap() {
|
||||
delete m_key;
|
||||
if (m_children != NULL) {
|
||||
for (int i = 0; i < 256; ++i) {
|
||||
delete (*m_children)[i];
|
||||
void InputMap::setHelper(Node &node, const char *encoding, int encodingLen, const Key &key) {
|
||||
if (encodingLen == 0) {
|
||||
node.key = key;
|
||||
} else {
|
||||
setHelper(getOrCreateChild(node, encoding[0]), encoding + 1, encodingLen - 1, key);
|
||||
}
|
||||
}
|
||||
|
||||
InputMap::Node &InputMap::getOrCreateChild(Node &node, unsigned char ch) {
|
||||
Node *ret = getChild(node, ch);
|
||||
if (ret != NULL) {
|
||||
return *ret;
|
||||
}
|
||||
if (node.childCount < Node::kTinyCount) {
|
||||
// Maintain sorted order for the sake of the InputMap dumping.
|
||||
int insertIndex = node.childCount;
|
||||
for (int i = 0; i < node.childCount; ++i) {
|
||||
if (ch < node.u.tiny.values[i]) {
|
||||
insertIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (int j = node.childCount; j > insertIndex; --j) {
|
||||
node.u.tiny.values[j] = node.u.tiny.values[j - 1];
|
||||
node.u.tiny.children[j] = node.u.tiny.children[j - 1];
|
||||
}
|
||||
node.u.tiny.values[insertIndex] = ch;
|
||||
node.u.tiny.children[insertIndex] = ret = m_nodePool.alloc();
|
||||
++node.childCount;
|
||||
return *ret;
|
||||
}
|
||||
if (node.childCount == Node::kTinyCount) {
|
||||
Branch *branch = m_branchPool.alloc();
|
||||
for (int i = 0; i < node.childCount; ++i) {
|
||||
branch->children[node.u.tiny.values[i]] = node.u.tiny.children[i];
|
||||
}
|
||||
node.u.branch = branch;
|
||||
}
|
||||
node.u.branch->children[ch] = ret = m_nodePool.alloc();
|
||||
++node.childCount;
|
||||
return *ret;
|
||||
}
|
||||
|
||||
// Find the longest matching key and node.
|
||||
int InputMap::lookupKey(const char *input, int inputSize,
|
||||
Key &keyOut, bool &incompleteOut) const {
|
||||
keyOut = kKeyZero;
|
||||
incompleteOut = false;
|
||||
|
||||
const Node *node = &m_root;
|
||||
InputMap::Key longestMatch = kKeyZero;
|
||||
int longestMatchLen = 0;
|
||||
|
||||
for (int i = 0; i < inputSize; ++i) {
|
||||
unsigned char ch = input[i];
|
||||
node = getChild(*node, ch);
|
||||
if (node == NULL) {
|
||||
keyOut = longestMatch;
|
||||
return longestMatchLen;
|
||||
} else if (node->hasKey()) {
|
||||
longestMatchLen = i + 1;
|
||||
longestMatch = node->key;
|
||||
}
|
||||
}
|
||||
delete [] m_children;
|
||||
keyOut = longestMatch;
|
||||
incompleteOut = node->childCount > 0;
|
||||
return longestMatchLen;
|
||||
}
|
||||
|
||||
void InputMap::set(const char *encoding, int encodingLen, const Key &key) {
|
||||
if (encodingLen == 0) {
|
||||
setKey(key);
|
||||
} else {
|
||||
getOrCreateChild(encoding[0])->set(encoding + 1, encodingLen - 1, key);
|
||||
}
|
||||
}
|
||||
|
||||
void InputMap::setKey(const Key &key) {
|
||||
delete m_key;
|
||||
m_key = new Key(key);
|
||||
}
|
||||
|
||||
InputMap *InputMap::getOrCreateChild(unsigned char ch) {
|
||||
if (m_children == NULL) {
|
||||
m_children = reinterpret_cast<InputMap*(*)[256]>(new InputMap*[256]);
|
||||
memset(m_children, 0, sizeof(InputMap*) * 256);
|
||||
}
|
||||
if ((*m_children)[ch] == NULL) {
|
||||
(*m_children)[ch] = new InputMap;
|
||||
}
|
||||
return (*m_children)[ch];
|
||||
}
|
||||
|
||||
void dumpInputMap(InputMap &inputMap) {
|
||||
void InputMap::dumpInputMap() const {
|
||||
std::string encoding;
|
||||
dumpInputMapHelper(inputMap, encoding);
|
||||
dumpInputMapHelper(m_root, encoding);
|
||||
}
|
||||
|
||||
void InputMap::dumpInputMapHelper(
|
||||
const Node &node, std::string &encoding) const {
|
||||
if (node.hasKey()) {
|
||||
trace("%s -> %s",
|
||||
encoding.c_str(),
|
||||
node.key.toString().c_str());
|
||||
}
|
||||
for (int i = 0; i < 256; ++i) {
|
||||
const Node *child = getChild(node, i);
|
||||
if (child != NULL) {
|
||||
size_t oldSize = encoding.size();
|
||||
if (!encoding.empty()) {
|
||||
encoding.push_back(' ');
|
||||
}
|
||||
char ctrlChar = decodeUnixCtrlChar(i);
|
||||
if (ctrlChar != '\0') {
|
||||
encoding.push_back('^');
|
||||
encoding.push_back(static_cast<char>(ctrlChar));
|
||||
} else if (i == ' ') {
|
||||
encoding.append("' '");
|
||||
} else {
|
||||
encoding.push_back(static_cast<char>(i));
|
||||
}
|
||||
dumpInputMapHelper(*child, encoding);
|
||||
encoding.resize(oldSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -21,38 +21,94 @@
|
||||
#ifndef INPUT_MAP_H
|
||||
#define INPUT_MAP_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "SimplePool.h"
|
||||
#include "../shared/WinptyAssert.h"
|
||||
|
||||
class InputMap {
|
||||
public:
|
||||
struct Key {
|
||||
int virtualKey;
|
||||
int unicodeChar;
|
||||
int keyState;
|
||||
uint16_t virtualKey;
|
||||
uint16_t unicodeChar;
|
||||
uint16_t keyState;
|
||||
|
||||
std::string toString() const;
|
||||
};
|
||||
|
||||
InputMap();
|
||||
~InputMap();
|
||||
void set(const char *encoding, int encodingLen, const Key &key);
|
||||
void setKey(const Key &key);
|
||||
const Key *getKey() const { return m_key; }
|
||||
bool hasChildren() const { return m_children != NULL; }
|
||||
InputMap *getChild(unsigned char ch) {
|
||||
return m_children != NULL ? (*m_children)[ch] : NULL;
|
||||
}
|
||||
InputMap *getOrCreateChild(unsigned char ch);
|
||||
private:
|
||||
struct Node;
|
||||
|
||||
struct Branch {
|
||||
Branch() {
|
||||
memset(&children, 0, sizeof(children));
|
||||
}
|
||||
|
||||
Node *children[256];
|
||||
};
|
||||
|
||||
struct Node {
|
||||
Node() : childCount(0) {
|
||||
Key zeroKey = { 0, 0, 0 };
|
||||
key = zeroKey;
|
||||
}
|
||||
|
||||
Key key;
|
||||
int childCount;
|
||||
enum { kTinyCount = 8 };
|
||||
union {
|
||||
Branch *branch;
|
||||
struct {
|
||||
unsigned char values[kTinyCount];
|
||||
Node *children[kTinyCount];
|
||||
} tiny;
|
||||
} u;
|
||||
|
||||
bool hasKey() const {
|
||||
return key.virtualKey != 0 || key.unicodeChar != 0;
|
||||
}
|
||||
};
|
||||
|
||||
private:
|
||||
const Key *m_key;
|
||||
InputMap *(*m_children)[256];
|
||||
SimplePool<Node, 256> m_nodePool;
|
||||
SimplePool<Branch, 8> m_branchPool;
|
||||
Node m_root;
|
||||
|
||||
public:
|
||||
void set(const char *encoding, int encodingLen, const Key &key);
|
||||
int lookupKey(const char *input, int inputSize,
|
||||
Key &keyOut, bool &incompleteOut) const;
|
||||
void dumpInputMap() const;
|
||||
|
||||
private:
|
||||
Node *getChild(Node &node, unsigned char ch) {
|
||||
return const_cast<Node*>(getChild(static_cast<const Node&>(node), ch));
|
||||
}
|
||||
|
||||
const Node *getChild(const Node &node, unsigned char ch) const {
|
||||
if (node.childCount <= Node::kTinyCount) {
|
||||
for (int i = 0; i < node.childCount; ++i) {
|
||||
if (node.u.tiny.values[i] == ch) {
|
||||
return node.u.tiny.children[i];
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
} else {
|
||||
return node.u.branch->children[ch];
|
||||
}
|
||||
}
|
||||
|
||||
void setHelper(Node &node, const char *encoding, int encodingLen, const Key &key);
|
||||
Node &getOrCreateChild(Node &node, unsigned char ch);
|
||||
void dumpInputMapHelper(const Node &node, std::string &encoding) const;
|
||||
};
|
||||
|
||||
const InputMap::Key kKeyZero = { 0, 0, 0 };
|
||||
|
||||
void dumpInputMap(InputMap &inputMap);
|
||||
|
||||
#endif // INPUT_MAP_H
|
||||
|
75
src/agent/SimplePool.h
Executable file
75
src/agent/SimplePool.h
Executable file
@ -0,0 +1,75 @@
|
||||
// Copyright (c) 2015 Ryan Prichard
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
|
||||
#ifndef SIMPLE_POOL_H
|
||||
#define SIMPLE_POOL_H
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "../shared/WinptyAssert.h"
|
||||
|
||||
template <typename T, size_t chunkSize>
|
||||
class SimplePool {
|
||||
public:
|
||||
~SimplePool();
|
||||
T *alloc();
|
||||
void clear();
|
||||
private:
|
||||
struct Chunk {
|
||||
size_t count;
|
||||
T *data;
|
||||
};
|
||||
std::vector<Chunk> m_chunks;
|
||||
};
|
||||
|
||||
template <typename T, size_t chunkSize>
|
||||
SimplePool<T, chunkSize>::~SimplePool() {
|
||||
clear();
|
||||
}
|
||||
|
||||
template <typename T, size_t chunkSize>
|
||||
void SimplePool<T, chunkSize>::clear() {
|
||||
for (size_t ci = 0; ci < m_chunks.size(); ++ci) {
|
||||
Chunk &chunk = m_chunks[ci];
|
||||
for (size_t ti = 0; ti < chunk.count; ++ti) {
|
||||
chunk.data[ti].~T();
|
||||
}
|
||||
free(chunk.data);
|
||||
}
|
||||
m_chunks.clear();
|
||||
}
|
||||
|
||||
template <typename T, size_t chunkSize>
|
||||
T *SimplePool<T, chunkSize>::alloc() {
|
||||
if (m_chunks.empty() || m_chunks.back().count == chunkSize) {
|
||||
T *newData = reinterpret_cast<T*>(malloc(sizeof(T) * chunkSize));
|
||||
ASSERT(newData != NULL);
|
||||
Chunk newChunk = { 0, newData };
|
||||
m_chunks.push_back(newChunk);
|
||||
}
|
||||
Chunk &chunk = m_chunks.back();
|
||||
T *ret = &chunk.data[chunk.count++];
|
||||
new (ret) T();
|
||||
return ret;
|
||||
}
|
||||
|
||||
#endif // SIMPLE_POOL_H
|
@ -49,6 +49,7 @@
|
||||
'agent/LargeConsoleRead.cc',
|
||||
'agent/NamedPipe.h',
|
||||
'agent/NamedPipe.cc',
|
||||
'agent/SimplePool.h',
|
||||
'agent/SmallRect.h',
|
||||
'agent/SmallRect.cc',
|
||||
'agent/Terminal.h',
|
||||
|
Loading…
Reference in New Issue
Block a user