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:
Ryan Prichard 2015-12-02 23:08:00 -06:00
parent 6795a6ee49
commit 850eb9ff3f
7 changed files with 279 additions and 137 deletions

View File

@ -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

View File

@ -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:

View File

@ -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) {

View File

@ -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);
}
}
}

View File

@ -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
View 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

View File

@ -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',