commit 41767be56615edd9bf45f90badba83d77d966a14
parent e92c15905936615e74361daea3178b64251ed15f
Author: dsp56300 <dsp56300@users.noreply.github.com>
Date: Sun, 8 Dec 2024 17:27:48 +0100
add DSP bridge
Diffstat:
205 files changed, 18928 insertions(+), 211 deletions(-)
diff --git a/base.cmake b/base.cmake
@@ -34,6 +34,7 @@ if(MSVC)
set(CMAKE_STATIC_LINKER_FLAGS_RELEASE "${CMAKE_STATIC_LINKER_FLAGS_RELEASE} /LTCG")
set(CMAKE_MODULE_LINKER_FLAGS_RELEASE "${CMAKE_MODULE_LINKER_FLAGS_RELEASE} /LTCG /DEBUG")
+ set(CMAKE_SHARED_LINKER_FLAGS_RELEASE "${CMAKE_MODULE_LINKER_FLAGS_RELEASE} /LTCG /DEBUG")
set(CMAKE_EXE_LINKER_FLAGS_RELEASE "${CMAKE_EXE_LINKER_FLAGS_RELEASE} /LTCG /DEBUG")
set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} /SUBSYSTEM:WINDOWS /SAFESEH:NO")
diff --git a/scripts/synths.cmake b/scripts/synths.cmake
@@ -4,6 +4,7 @@ set(synths
gearmulator_SYNTH_VAVRA
gearmulator_SYNTH_XENIA
gearmulator_SYNTH_NODALRED2X
+ gearmulator_COMPONENT_DSPBRIDGE
)
set(gearmulator_SYNTH_OSIRUS_name Osirus)
@@ -11,12 +12,14 @@ set(gearmulator_SYNTH_OSTIRUS_name OsTIrus)
set(gearmulator_SYNTH_VAVRA_name Vavra)
set(gearmulator_SYNTH_XENIA_name Xenia)
set(gearmulator_SYNTH_NODALRED2X_name NodalRed2x)
+set(gearmulator_COMPONENT_DSPBRIDGE_name DSPBridge)
set(gearmulator_SYNTH_OSIRUS_folder osirus)
set(gearmulator_SYNTH_OSTIRUS_folder ostirus)
set(gearmulator_SYNTH_VAVRA_folder vavra)
set(gearmulator_SYNTH_XENIA_folder xenia)
set(gearmulator_SYNTH_NODALRED2X_folder nodalred2x)
+set(gearmulator_COMPONENT_DSPBRIDGE_folder dspbridge)
macro(validateToggle NAME)
if(NOT DEFINED ${NAME} OR (NOT ${${NAME}} STREQUAL "on" AND NOT ${${NAME}} STREQUAL "off"))
diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt
@@ -39,6 +39,12 @@ add_subdirectory(libresample)
include(macsetup.cmake)
include(skins.cmake)
+# ----------------- network bridge
+
+add_subdirectory(ptypes EXCLUDE_FROM_ALL)
+add_subdirectory(networkLib EXCLUDE_FROM_ALL)
+add_subdirectory(bridge)
+
# ----------------- Try to install VST2 SDK
include(findvst2.cmake)
diff --git a/source/baseLib/CMakeLists.txt b/source/baseLib/CMakeLists.txt
@@ -9,6 +9,7 @@ set(SOURCES
configFile.cpp configFile.h
hybridcontainer.h
md5.cpp md5.h
+ propertyMap.cpp propertyMap.h
semaphore.h
)
diff --git a/source/baseLib/binarystream.h b/source/baseLib/binarystream.h
@@ -103,6 +103,8 @@ namespace baseLib
return !eof();
}
+ auto& getVector() { return m_vector; }
+
private:
size_t size() const { return m_fixedSize ? m_size : m_vector.size(); }
@@ -149,6 +151,10 @@ namespace baseLib
{
}
+ explicit BinaryStream(const size_t _capacity) : StreamBuffer(_capacity)
+ {
+ }
+
template<typename T> explicit BinaryStream(const std::vector<T>& _data)
{
Base::write(reinterpret_cast<const uint8_t*>(_data.data()), _data.size() * sizeof(T));
@@ -208,6 +214,8 @@ namespace baseLib
void setWritePos(const uint32_t _pos) { seekp(_pos); }
void setReadPos(const uint32_t _pos) { seekg(_pos); }
+
+ using StreamBuffer::getVector;
// ___________________________________
// write
diff --git a/source/baseLib/commandline.cpp b/source/baseLib/commandline.cpp
@@ -1,8 +1,5 @@
#include"commandline.h"
-#include <cmath>
-#include <stdexcept>
-
namespace baseLib
{
CommandLine::CommandLine(const int _argc, char* _argv[])
@@ -18,7 +15,7 @@ namespace baseLib
if (arg[0] == '-')
{
if (!currentArg.empty())
- m_args.insert(currentArg);
+ add(currentArg);
currentArg = arg.substr(1);
}
@@ -26,61 +23,17 @@ namespace baseLib
{
if (!currentArg.empty())
{
- m_argsWithValues.insert(std::make_pair(currentArg, arg));
+ add(currentArg, arg);
currentArg.clear();
}
else
{
- m_args.insert(arg);
+ add(arg);
}
}
}
if (!currentArg.empty())
- m_args.insert(currentArg);
- }
-
- std::string CommandLine::tryGet(const std::string& _key, const std::string& _value) const
- {
- const auto it = m_argsWithValues.find(_key);
- if (it == m_argsWithValues.end())
- return _value;
- return it->second;
- }
-
- std::string CommandLine::get(const std::string& _key, const std::string& _default/* = {}*/) const
- {
- const auto it = m_argsWithValues.find(_key);
- if (it == m_argsWithValues.end())
- return _default;
- return it->second;
- }
-
- float CommandLine::getFloat(const std::string& _key, const float _default/* = 0.0f*/) const
- {
- const std::string stringResult = get(_key);
-
- if (stringResult.empty())
- return _default;
-
- const double result = atof(stringResult.c_str());
- if (std::isinf(result) || std::isnan(result))
- {
- return _default;
- }
- return static_cast<float>(result);
- }
-
- int CommandLine::getInt(const std::string& _key, const int _default/* = 0*/) const
- {
- const std::string stringResult = get(_key);
- if (stringResult.empty())
- return _default;
- return atoi(stringResult.c_str());
- }
-
- bool CommandLine::contains(const std::string& _key) const
- {
- return m_args.find(_key) != m_args.end() || m_argsWithValues.find(_key) != m_argsWithValues.end();
+ add(currentArg);
}
}
\ No newline at end of file
diff --git a/source/baseLib/commandline.h b/source/baseLib/commandline.h
@@ -1,28 +1,12 @@
#pragma once
-#include <string>
-#include <map>
-#include <set>
+#include "propertyMap.h"
namespace baseLib
{
- class CommandLine
+ class CommandLine : public PropertyMap
{
public:
CommandLine(int _argc, char* _argv[]);
-
- std::string tryGet(const std::string& _key, const std::string& _value = std::string()) const;
-
- std::string get(const std::string& _key, const std::string& _default = {}) const;
-
- float getFloat(const std::string& _key, float _default = 0.0f) const;
-
- int getInt(const std::string& _key, int _default = 0) const;
-
- bool contains(const std::string& _key) const;
-
- private:
- std::map<std::string, std::string> m_argsWithValues;
- std::set<std::string> m_args;
};
}
diff --git a/source/baseLib/configFile.cpp b/source/baseLib/configFile.cpp
@@ -4,27 +4,30 @@
namespace baseLib
{
- static bool needsTrim(char _c)
+ namespace
{
- switch (_c)
+ bool needsTrim(const char _c)
{
- case ' ':
- case '\r':
- case '\n':
- case '\t':
- return true;
- default:
- return false;
+ switch (_c)
+ {
+ case ' ':
+ case '\r':
+ case '\n':
+ case '\t':
+ return true;
+ default:
+ return false;
+ }
}
- }
- static std::string trim(std::string _s)
- {
- while (!_s.empty() && needsTrim(_s.front()))
- _s = _s.substr(1);
- while (!_s.empty() && needsTrim(_s.back()))
- _s = _s.substr(0, _s.size() - 1);
- return _s;
+ std::string trim(std::string _s)
+ {
+ while (!_s.empty() && needsTrim(_s.front()))
+ _s = _s.substr(1);
+ while (!_s.empty() && needsTrim(_s.back()))
+ _s = _s.substr(0, _s.size() - 1);
+ return _s;
+ }
}
ConfigFile::ConfigFile(const char* _filename)
@@ -32,7 +35,7 @@ namespace baseLib
std::ifstream file(_filename, std::ios::in);
if (!file.is_open())
- throw std::runtime_error("Failed to open config file " + std::string(_filename));
+ return;
std::string line;
@@ -53,7 +56,11 @@ namespace baseLib
const auto key = trim(line.substr(0, posEq));
const auto val = trim(line.substr(posEq + 1));
- m_values.emplace_back(key, val);
+ add(key, val);
}
}
-}
-\ No newline at end of file
+
+ ConfigFile::ConfigFile(const std::string& _filename) : ConfigFile(_filename.c_str())
+ {
+ }
+}
diff --git a/source/baseLib/configFile.h b/source/baseLib/configFile.h
@@ -1,20 +1,13 @@
#pragma once
-#include <string>
-#include <vector>
+#include "propertyMap.h"
namespace baseLib
{
- class ConfigFile
+ class ConfigFile : public PropertyMap
{
public:
explicit ConfigFile(const char* _filename);
-
- const std::vector<std::pair<std::string, std::string>>& getValues() const
- {
- return m_values;
- }
- private:
- std::vector<std::pair<std::string, std::string>> m_values;
+ explicit ConfigFile(const std::string& _filename);
};
}
diff --git a/source/baseLib/md5.cpp b/source/baseLib/md5.cpp
@@ -121,14 +121,15 @@ namespace baseLib
_h3 += d;
}
+ }
- // cleanup
- // free(msg);
+ MD5::MD5(const std::vector<uint8_t>& _data) : MD5(_data.data(), static_cast<uint32_t>(_data.size()))
+ {
}
- MD5::MD5(const std::vector<uint8_t>& _data)
+ MD5::MD5(const uint8_t* _data, const uint32_t _size)
{
- md5(m_h[0], m_h[1], m_h[2], m_h[3], _data.data(), static_cast<uint32_t>(_data.size()));
+ md5(m_h[0], m_h[1], m_h[2], m_h[3], _data, _size);
}
std::string MD5::toString() const
diff --git a/source/baseLib/md5.h b/source/baseLib/md5.h
@@ -47,6 +47,7 @@ namespace baseLib
}
explicit MD5(const std::vector<uint8_t>& _data);
+ explicit MD5(const uint8_t* _data, uint32_t _size);
MD5() : m_h({0,0,0,0}) {}
diff --git a/source/baseLib/propertyMap.cpp b/source/baseLib/propertyMap.cpp
@@ -0,0 +1,86 @@
+#include "propertyMap.h"
+
+#include <cmath>
+
+namespace baseLib
+{
+ bool PropertyMap::add(const std::string& _key, const std::string& _value)
+ {
+ if (m_argsWithValues.find(_key) != m_argsWithValues.end())
+ return false;
+ m_argsWithValues.insert(std::make_pair(_key, _value));
+ return true;
+ }
+
+ bool PropertyMap::add(const std::string& _key)
+ {
+ return m_args.insert(_key).second;
+ }
+
+ bool PropertyMap::add(const PropertyMap& _other, const bool _overwrite)
+ {
+ bool changed = false;
+
+ for (const auto& [key, val] : _other.getArgsWithValues())
+ {
+ if (_overwrite || m_argsWithValues.find(key) == m_argsWithValues.end())
+ {
+ m_argsWithValues[key] = val;
+ changed = true;
+ }
+ }
+ for (const auto& a : _other.getArgs())
+ {
+ if (_overwrite || m_args.find(a) == m_args.end())
+ {
+ m_args.insert(a);
+ changed = true;
+ }
+ }
+ return changed;
+ }
+
+ std::string PropertyMap::tryGet(const std::string& _key, const std::string& _value) const
+ {
+ const auto it = m_argsWithValues.find(_key);
+ if (it == m_argsWithValues.end())
+ return _value;
+ return it->second;
+ }
+
+ std::string PropertyMap::get(const std::string& _key, const std::string& _default/* = {}*/) const
+ {
+ const auto it = m_argsWithValues.find(_key);
+ if (it == m_argsWithValues.end())
+ return _default;
+ return it->second;
+ }
+
+ float PropertyMap::getFloat(const std::string& _key, const float _default/* = 0.0f*/) const
+ {
+ const std::string stringResult = get(_key);
+
+ if (stringResult.empty())
+ return _default;
+
+ const double result = atof(stringResult.c_str());
+ if (std::isinf(result) || std::isnan(result))
+ {
+ return _default;
+ }
+ return static_cast<float>(result);
+ }
+
+ int PropertyMap::getInt(const std::string& _key, const int _default/* = 0*/) const
+ {
+ const std::string stringResult = get(_key);
+ if (stringResult.empty())
+ return _default;
+ return atoi(stringResult.c_str());
+ }
+
+ bool PropertyMap::contains(const std::string& _key) const
+ {
+ return m_args.find(_key) != m_args.end() || m_argsWithValues.find(_key) != m_argsWithValues.end();
+ }
+}
diff --git a/source/baseLib/propertyMap.h b/source/baseLib/propertyMap.h
@@ -0,0 +1,35 @@
+#pragma once
+
+#include <string>
+#include <map>
+#include <set>
+
+namespace baseLib
+{
+ class PropertyMap
+ {
+ public:
+ bool add(const std::string& _key, const std::string& _value);
+ bool add(const std::string& _key);
+ bool add(const PropertyMap& _other, bool _overwrite = false);
+
+ std::string tryGet(const std::string& _key, const std::string& _value = std::string()) const;
+
+ std::string get(const std::string& _key, const std::string& _default = {}) const;
+
+ float getFloat(const std::string& _key, float _default = 0.0f) const;
+
+ int getInt(const std::string& _key, int _default = 0) const;
+
+ bool contains(const std::string& _key) const;
+
+ const auto& getArgsWithValues() const { return m_argsWithValues; }
+ const auto& getArgs() const { return m_args; }
+
+ bool empty() const { return m_argsWithValues.empty() && m_args.empty(); }
+
+ private:
+ std::map<std::string, std::string> m_argsWithValues;
+ std::set<std::string> m_args;
+ };
+}
diff --git a/source/bridge/CMakeLists.txt b/source/bridge/CMakeLists.txt
@@ -0,0 +1,3 @@
+add_subdirectory(bridgeLib EXCLUDE_FROM_ALL)
+add_subdirectory(client)
+add_subdirectory(server)
diff --git a/source/bridge/bridgeLib/CMakeLists.txt b/source/bridge/bridgeLib/CMakeLists.txt
@@ -0,0 +1,26 @@
+cmake_minimum_required(VERSION 3.10)
+
+project(bridgeLib)
+
+add_library(bridgeLib STATIC)
+
+set(SOURCES
+ audioBuffers.cpp audioBuffers.h
+ command.cpp command.h
+ commandReader.cpp commandReader.h
+ commands.cpp commands.h
+ commandStruct.cpp commandStruct.h
+ commandWriter.cpp commandWriter.h
+ error.cpp error.h
+ tcpConnection.cpp tcpConnection.h
+ types.h
+)
+
+target_sources(bridgeLib PRIVATE ${SOURCES})
+
+source_group("source" FILES ${SOURCES})
+
+target_link_libraries(bridgeLib PUBLIC networkLib synthLib)
+
+target_include_directories(bridgeLib PUBLIC ../)
+set_property(TARGET bridgeLib PROPERTY FOLDER "Bridge")
diff --git a/source/bridge/bridgeLib/audioBuffers.cpp b/source/bridge/bridgeLib/audioBuffers.cpp
@@ -0,0 +1,70 @@
+#include "audioBuffers.h"
+
+namespace bridgeLib
+{
+ AudioBuffers::AudioBuffers() = default;
+
+ void AudioBuffers::writeInput(const synthLib::TAudioInputs& _inputs, const uint32_t _size)
+ {
+ m_inputSize += _size;
+
+ for(size_t c=0; c<_inputs.size(); ++c)
+ {
+ auto& in = _inputs[c];
+
+ if(!in)
+ continue;
+
+ for(uint32_t i=0; i<_size; ++i)
+ m_inputBuffers[c].push_back(in[i]);
+ }
+ }
+
+ void AudioBuffers::readInput(const uint32_t _channel, std::vector<float>& _data, const uint32_t _numSamples)
+ {
+ for(uint32_t i=0; i<_numSamples; ++i)
+ _data[i] = m_inputBuffers[_channel].pop_front();
+ }
+
+ void AudioBuffers::readOutput(const synthLib::TAudioOutputs& _outputs, const uint32_t _size)
+ {
+ assert(m_outputSize >= _size);
+ m_outputSize -= _size;
+
+ for(size_t c=0; c<_outputs.size(); ++c)
+ {
+ auto* out = _outputs[c];
+
+ if(!out)
+ continue;
+
+ for(uint32_t i=0; i<_size; ++i)
+ out[i] = m_outputBuffers[c].pop_front();
+ }
+ }
+
+ void AudioBuffers::writeOutput(const uint32_t _channel, const std::vector<float>& _data, const uint32_t _numSamples)
+ {
+ for(uint32_t i=0; i<_numSamples; ++i)
+ m_outputBuffers[_channel].push_back(_data[i]);
+ }
+
+ void AudioBuffers::setLatency(const uint32_t _newLatency, const uint32_t _numSamplesToKeep)
+ {
+ while(_newLatency > m_latency)
+ {
+ for (auto& in : m_inputBuffers)
+ in.push_back(0.0f);
+ ++m_latency;
+ ++m_inputSize;
+ }
+
+ while(_newLatency < m_latency && m_inputBuffers.front().size() > _numSamplesToKeep)
+ {
+ for (auto& in : m_inputBuffers)
+ in.pop_front();
+ --m_latency;
+ --m_inputSize;
+ }
+ }
+}
diff --git a/source/bridge/bridgeLib/audioBuffers.h b/source/bridge/bridgeLib/audioBuffers.h
@@ -0,0 +1,50 @@
+#pragma once
+
+#include "baseLib/binarystream.h"
+#include "dsp56kEmu/ringbuffer.h"
+
+#include "synthLib/audioTypes.h"
+
+namespace bridgeLib
+{
+ class AudioBuffers
+ {
+ public:
+ static constexpr uint32_t BufferSize = 16384;
+
+ using RingBufferIn = dsp56k::RingBuffer<float, BufferSize, false, true>;
+ using RingBufferOut = dsp56k::RingBuffer<float, BufferSize, false, true>;
+
+ AudioBuffers();
+
+ uint32_t getInputSize() const { return m_inputSize; }
+ uint32_t getOutputSize() const { return m_outputSize; }
+ auto getLatency() const { return m_latency; }
+
+ void onInputRead(const uint32_t _size)
+ {
+ assert(m_inputSize >= _size);
+ m_inputSize -= _size;
+ }
+
+ void onOutputWritten(const uint32_t _size)
+ {
+ m_outputSize += _size;
+ }
+
+ void writeInput(const synthLib::TAudioInputs& _inputs, uint32_t _size);
+ void readInput(uint32_t _channel, std::vector<float>& _data, uint32_t _numSamples);
+
+ void readOutput(const synthLib::TAudioOutputs& _outputs, uint32_t _size);
+ void writeOutput(uint32_t _channel, const std::vector<float>& _data, uint32_t _numSamples);
+ void setLatency(uint32_t _newLatency, uint32_t _numSamplesToKeep);
+
+ private:
+ std::array<RingBufferIn, std::tuple_size_v<synthLib::TAudioInputs>> m_inputBuffers;
+ std::array<RingBufferOut, std::tuple_size_v<synthLib::TAudioOutputs>> m_outputBuffers;
+
+ uint32_t m_inputSize = 0;
+ uint32_t m_outputSize = 0;
+ uint32_t m_latency = 0;
+ };
+}
diff --git a/source/bridge/bridgeLib/command.cpp b/source/bridge/bridgeLib/command.cpp
@@ -0,0 +1,22 @@
+#include "command.h"
+
+namespace bridgeLib
+{
+ enum class HostEndian
+ {
+ Big,
+ Little
+ };
+
+ constexpr HostEndian hostEndian()
+ {
+ constexpr uint32_t test32 = 0x01020304;
+ constexpr uint8_t test8 = static_cast<const uint8_t&>(test32);
+
+ static_assert(test8 == 0x01 || test8 == 0x04, "unable to determine endianess");
+
+ return test8 == 0x01 ? HostEndian::Big : HostEndian::Little;
+ }
+
+ static_assert(hostEndian() == HostEndian::Little, "big endian systems not supported");
+}
diff --git a/source/bridge/bridgeLib/command.h b/source/bridge/bridgeLib/command.h
@@ -0,0 +1,16 @@
+#pragma once
+
+#include <cstdint>
+#include <string>
+
+namespace bridgeLib
+{
+ template<size_t N, std::enable_if_t<N == 5, void*> = nullptr>
+ constexpr uint32_t cmd(const char (&_cmd)[N])
+ {
+ return (static_cast<uint32_t>(_cmd[0]) << 24) |
+ (static_cast<uint32_t>(_cmd[1]) << 16) |
+ (static_cast<uint32_t>(_cmd[2]) << 8) |
+ (static_cast<uint32_t>(_cmd[3]));
+ }
+}
diff --git a/source/bridge/bridgeLib/commandReader.cpp b/source/bridge/bridgeLib/commandReader.cpp
@@ -0,0 +1,57 @@
+#include "commandReader.h"
+
+#include "command.h"
+#include "dsp56300/source/dsp56kEmu/logging.h"
+#include "networkLib/stream.h"
+
+namespace bridgeLib
+{
+ CommandReader::CommandReader(CommandCallback&& _callback) : m_stream(512 * 1024), m_commandCallback(std::move(_callback))
+ {
+ }
+
+ void CommandReader::read(networkLib::Stream& _stream)
+ {
+ // read command (4 bytes)
+ char temp[5]{0,0,0,0,0};
+
+ _stream.read(temp, 4);
+ const auto command = static_cast<Command>(cmd(temp));
+
+ // read size (4 bytes)
+ uint32_t size;
+ _stream.read(&size, sizeof(size));
+
+ // read data (n bytes)
+ m_stream.getVector().resize(size);
+ _stream.read(m_stream.getVector().data(), size);
+
+// LOG("Recv cmd " << commandToString(command) << ", len " << size);
+ m_stream.setReadPos(0);
+ handleCommand(command, m_stream);
+ }
+
+ void CommandReader::read(baseLib::BinaryStream& _in)
+ {
+ // read command (4 bytes)
+ char temp[5]{0,0,0,0,0};
+
+ _in.read(temp[0]);
+ _in.read(temp[1]);
+ _in.read(temp[2]);
+ _in.read(temp[3]);
+
+ const auto command = cmd(temp);
+
+ // read size (4 bytes)
+ const uint32_t size = _in.read<uint32_t>();
+
+ // read data (n bytes)
+ m_stream.getVector().resize(size);
+ for(size_t i=0; i<size; ++i)
+ m_stream.getVector()[i] = _in.read<uint8_t>();
+
+ m_stream.setReadPos(0);
+ handleCommand(static_cast<Command>(command), m_stream);
+ }
+}
diff --git a/source/bridge/bridgeLib/commandReader.h b/source/bridge/bridgeLib/commandReader.h
@@ -0,0 +1,34 @@
+#pragma once
+
+#include "commands.h"
+
+#include "baseLib/binarystream.h"
+
+namespace networkLib
+{
+ class Stream;
+}
+
+namespace bridgeLib
+{
+ class CommandReader
+ {
+ public:
+ using CommandCallback = std::function<void(Command, baseLib::BinaryStream&)>;
+
+ CommandReader(CommandCallback&& _callback);
+ virtual ~CommandReader() = default;
+
+ void read(networkLib::Stream& _stream);
+ void read(baseLib::BinaryStream& _in);
+
+ virtual void handleCommand(Command _command, baseLib::BinaryStream& _in)
+ {
+ m_commandCallback(_command, _in);
+ }
+
+ private:
+ baseLib::BinaryStream m_stream;
+ CommandCallback m_commandCallback;
+ };
+}
diff --git a/source/bridge/bridgeLib/commandStruct.cpp b/source/bridge/bridgeLib/commandStruct.cpp
diff --git a/source/bridge/bridgeLib/commandStruct.h b/source/bridge/bridgeLib/commandStruct.h
@@ -0,0 +1,18 @@
+#pragma once
+
+namespace baseLib
+{
+ class BinaryStream;
+}
+
+namespace bridgeLib
+{
+ class CommandStruct
+ {
+ public:
+ virtual ~CommandStruct() = default;
+
+ virtual baseLib::BinaryStream& write(baseLib::BinaryStream& _s) const = 0;
+ virtual baseLib::BinaryStream& read(baseLib::BinaryStream& _s) = 0;
+ };
+}
diff --git a/source/bridge/bridgeLib/commandWriter.cpp b/source/bridge/bridgeLib/commandWriter.cpp
@@ -0,0 +1,64 @@
+#include "commandWriter.h"
+
+#include <array>
+
+#include "networkLib/stream.h"
+
+namespace bridgeLib
+{
+ CommandWriter::CommandWriter() : m_stream(512 * 1024)
+ {
+ }
+
+ baseLib::BinaryStream& CommandWriter::build(const Command _command)
+ {
+ m_stream.setWritePos(0);
+ m_command = _command;
+
+ return m_stream;
+ }
+
+ baseLib::BinaryStream& CommandWriter::build(const Command _command, const CommandStruct& _data)
+ {
+ return _data.write(build(_command));
+ }
+
+ void CommandWriter::write(networkLib::Stream& _stream, const bool _flush)
+ {
+ // send command (4 bytes)
+ std::array<char,5> buf;
+ commandToBuffer(buf, m_command);
+ _stream.write(buf.data(), 4);
+
+ // send size (4 bytes)
+ const auto size = m_stream.getWritePos();
+ _stream.write(&size, sizeof(size));
+
+ // send data (size bytes)
+ const auto* data = m_stream.getVector().data();
+ _stream.write(data, size);
+
+ if(_flush)
+ _stream.flush();
+ }
+
+ void CommandWriter::write(baseLib::BinaryStream& _out)
+ {
+ // send command (4 bytes)
+ std::array<char,5> buf;
+ commandToBuffer(buf, m_command);
+ _out.write(buf[0]);
+ _out.write(buf[1]);
+ _out.write(buf[2]);
+ _out.write(buf[3]);
+
+ // send size (4 bytes)
+ const auto size = m_stream.getWritePos();
+ _out.write(size);
+
+ // send data (size bytes)
+ const auto* data = m_stream.getVector().data();
+ for(uint32_t i=0; i<size; ++i)
+ _out.write(m_stream.getVector()[i]);
+ }
+}
diff --git a/source/bridge/bridgeLib/commandWriter.h b/source/bridge/bridgeLib/commandWriter.h
@@ -0,0 +1,30 @@
+#pragma once
+
+#include "commands.h"
+
+#include "baseLib/binarystream.h"
+
+namespace networkLib
+{
+ class Stream;
+}
+
+namespace bridgeLib
+{
+ class CommandWriter
+ {
+ public:
+ CommandWriter();
+
+ baseLib::BinaryStream& build(Command _command);
+
+ baseLib::BinaryStream& build(const Command _command, const CommandStruct& _data);
+
+ void write(networkLib::Stream& _stream, bool _flush = true);
+ void write(baseLib::BinaryStream& _out);
+
+ private:
+ baseLib::BinaryStream m_stream;
+ Command m_command = Command::Invalid;
+ };
+}
diff --git a/source/bridge/bridgeLib/commands.cpp b/source/bridge/bridgeLib/commands.cpp
@@ -0,0 +1,187 @@
+#include "commands.h"
+
+#include <array>
+
+#include "baseLib/binarystream.h"
+
+namespace bridgeLib
+{
+ std::string commandToString(const Command _command)
+ {
+ std::array<char, 5> temp;
+ commandToBuffer(temp, _command);
+ return {temp.data()};
+ }
+
+ void commandToBuffer(std::array<char, 5>& _buffer, Command _command)
+ {
+ const auto c = static_cast<uint32_t>(_command);
+ _buffer[0] = static_cast<char>((c >> 24) & 0xff);
+ _buffer[1] = static_cast<char>((c >> 16) & 0xff);
+ _buffer[2] = static_cast<char>((c >> 8) & 0xff);
+ _buffer[3] = static_cast<char>((c) & 0xff);
+ _buffer[4] = 0;
+ }
+
+ baseLib::BinaryStream& Error::write(baseLib::BinaryStream& _s) const
+ {
+ _s.write<uint32_t>(static_cast<uint32_t>(code));
+ _s.write(msg);
+ return _s;
+ }
+
+ baseLib::BinaryStream& Error::read(baseLib::BinaryStream& _s)
+ {
+ code = static_cast<ErrorCode>(_s.read<uint32_t>());
+ msg = _s.readString();
+ return _s;
+ }
+
+ baseLib::BinaryStream& ServerInfo::write(baseLib::BinaryStream& _s) const
+ {
+ _s.write(protocolVersion);
+ _s.write(portUdp);
+ _s.write(portTcp);
+ return _s;
+ }
+
+ baseLib::BinaryStream& ServerInfo::read(baseLib::BinaryStream& _s)
+ {
+ _s.read(protocolVersion);
+ _s.read(portUdp);
+ _s.read(portTcp);
+ return _s;
+ }
+
+ baseLib::BinaryStream& PluginDesc::write(baseLib::BinaryStream& _s) const
+ {
+ _s.write(protocolVersion);
+ _s.write(pluginName);
+ _s.write(pluginVersion);
+ _s.write(plugin4CC);
+ _s.write(sessionId);
+ return _s;
+ }
+
+ baseLib::BinaryStream& PluginDesc::read(baseLib::BinaryStream& _s)
+ {
+ _s.read(protocolVersion);
+ pluginName = _s.readString();
+ _s.read(pluginVersion);
+ plugin4CC = _s.readString();
+ _s.read(sessionId);
+ return _s;
+ }
+
+ baseLib::BinaryStream& DeviceCreateParams::read(baseLib::BinaryStream& _s)
+ {
+ _s.read(params.preferredSamplerate);
+ _s.read(params.hostSamplerate);
+ params.romName = _s.readString();
+ _s.read(params.romData);
+ _s.read(params.romHash);
+ _s.read(params.customData);
+ return _s;
+ }
+
+ baseLib::BinaryStream& DeviceCreateParams::write(baseLib::BinaryStream& _s) const
+ {
+ _s.write(params.preferredSamplerate);
+ _s.write(params.hostSamplerate);
+ _s.write(params.romName);
+ _s.write(params.romData);
+ _s.write(params.romHash);
+ _s.write(params.customData);
+ return _s;
+ }
+
+ baseLib::BinaryStream& DeviceDesc::write(baseLib::BinaryStream& _s) const
+ {
+ _s.write(samplerate);
+ _s.write(outChannels);
+ _s.write(inChannels);
+ _s.write(dspClockPercent);
+ _s.write(dspClockHz);
+ _s.write(latencyInToOut);
+ _s.write(latencyMidiToOut);
+ _s.write(preferredSamplerates);
+ _s.write(supportedSamplerates);
+ return _s;
+ }
+
+ baseLib::BinaryStream& DeviceDesc::read(baseLib::BinaryStream& _s)
+ {
+ _s.read(samplerate);
+ _s.read(outChannels);
+ _s.read(inChannels);
+ _s.read(dspClockPercent);
+ _s.read(dspClockHz);
+ _s.read(latencyInToOut);
+ _s.read(latencyMidiToOut);
+ _s.read(preferredSamplerates);
+ _s.read(supportedSamplerates);
+ return _s;
+ }
+
+ baseLib::BinaryStream& RequestDeviceState::write(baseLib::BinaryStream& _s) const
+ {
+ _s.write(static_cast<uint32_t>(type));
+ return _s;
+ }
+
+ baseLib::BinaryStream& RequestDeviceState::read(baseLib::BinaryStream& _s)
+ {
+ type = static_cast<synthLib::StateType>(_s.read<uint32_t>());
+ return _s;
+ }
+
+ baseLib::BinaryStream& DeviceState::write(baseLib::BinaryStream& _s) const
+ {
+ _s.write(static_cast<uint32_t>(type));
+ _s.write(state);
+ return _s;
+ }
+
+ baseLib::BinaryStream& DeviceState::read(baseLib::BinaryStream& _s)
+ {
+ type = static_cast<synthLib::StateType>(_s.read<uint32_t>());
+ _s.read(state);
+ return _s;
+ }
+
+ baseLib::BinaryStream& SetSamplerate::write(baseLib::BinaryStream& _s) const
+ {
+ _s.write(samplerate);
+ return _s;
+ }
+
+ baseLib::BinaryStream& SetSamplerate::read(baseLib::BinaryStream& _s)
+ {
+ _s.read(samplerate);
+ return _s;
+ }
+
+ baseLib::BinaryStream& SetUnknownCustomData::write(baseLib::BinaryStream& _s) const
+ {
+ _s.write(data);
+ return _s;
+ }
+
+ baseLib::BinaryStream& SetUnknownCustomData::read(baseLib::BinaryStream& _s)
+ {
+ _s.read(data);
+ return _s;
+ }
+
+ baseLib::BinaryStream& SetDspClockPercent::write(baseLib::BinaryStream& _s) const
+ {
+ _s.write(percent);
+ return _s;
+ }
+
+ baseLib::BinaryStream& SetDspClockPercent::read(baseLib::BinaryStream& _s)
+ {
+ _s.read(percent);
+ return _s;
+ }
+}
diff --git a/source/bridge/bridgeLib/commands.h b/source/bridge/bridgeLib/commands.h
@@ -0,0 +1,174 @@
+#pragma once
+
+#include <cstdint>
+
+#include "command.h"
+#include "commandStruct.h"
+#include "error.h"
+#include "types.h"
+#include "baseLib/md5.h"
+#include "synthLib/device.h"
+#include "synthLib/deviceTypes.h"
+
+namespace bridgeLib
+{
+ enum class ErrorCode : uint32_t;
+
+ enum class Command : uint32_t
+ {
+ Invalid = 0,
+
+ Ping = cmd("ping"),
+ Pong = cmd("pong"),
+
+ PluginInfo = cmd("PInf"),
+ ServerInfo = cmd("SInf"),
+
+ Error = cmd("Erro"),
+
+ DeviceInfo = cmd("DevI"),
+
+ DeviceCreateParams = cmd("DCrP"),
+ RequestRom = cmd("ROMr"),
+
+ Midi = cmd("MIDI"),
+ Audio = cmd("Wave"),
+
+ DeviceState = cmd("DvSt"),
+ RequestDeviceState = cmd("RqDS"),
+
+ SetSamplerate = cmd("SmpR"),
+
+ SetUnknownCustomData = cmd("UnkD"),
+
+ SetDspClockPercent = cmd("DspC")
+ };
+
+ std::string commandToString(Command _command);
+ void commandToBuffer(std::array<char,5>& _buffer, Command _command);
+
+ struct Error : CommandStruct
+ {
+ ErrorCode code = ErrorCode::Ok;
+ std::string msg;
+
+ baseLib::BinaryStream& write(baseLib::BinaryStream& _s) const override;
+ baseLib::BinaryStream& read(baseLib::BinaryStream& _s) override;
+ };
+
+ struct ServerInfo : CommandStruct
+ {
+ uint32_t protocolVersion;
+ uint32_t portUdp;
+ uint32_t portTcp;
+
+ baseLib::BinaryStream& write(baseLib::BinaryStream& _s) const override;
+ baseLib::BinaryStream& read(baseLib::BinaryStream& _s) override;
+ };
+
+ struct PluginDesc : CommandStruct
+ {
+ uint32_t protocolVersion = g_protocolVersion;
+ std::string pluginName;
+ uint32_t pluginVersion = 0;
+ std::string plugin4CC;
+ SessionId sessionId = 0;
+
+ PluginDesc()
+ {
+ pluginName.reserve(32);
+ plugin4CC.reserve(32);
+ }
+
+ baseLib::BinaryStream& write(baseLib::BinaryStream& _s) const override;
+ baseLib::BinaryStream& read(baseLib::BinaryStream& _s) override;
+
+ bool operator == (const PluginDesc& _desc) const
+ {
+ return
+ pluginVersion == _desc.pluginVersion &&
+ plugin4CC == _desc.plugin4CC &&
+ pluginName == _desc.pluginName &&
+ protocolVersion == _desc.protocolVersion;
+ }
+
+ bool operator < (const PluginDesc& _desc) const
+ {
+ if(pluginVersion < _desc.pluginVersion) return true;
+ if(pluginVersion > _desc.pluginVersion) return false;
+ if(plugin4CC < _desc.plugin4CC) return true;
+ if(plugin4CC > _desc.plugin4CC) return false;
+ if(pluginName < _desc.pluginName) return true;
+ if(pluginName > _desc.pluginName) return false;
+ if(protocolVersion < _desc.protocolVersion) return true;
+ return false;
+ }
+ };
+
+ struct DeviceCreateParams : CommandStruct
+ {
+ synthLib::DeviceCreateParams params;
+
+ baseLib::BinaryStream& read(baseLib::BinaryStream& _s) override;
+ baseLib::BinaryStream& write(baseLib::BinaryStream& _s) const override;
+ };
+
+ struct DeviceDesc : CommandStruct
+ {
+ float samplerate = 0.0f;
+ uint32_t outChannels = 0;
+ uint32_t inChannels = 0;
+ uint32_t dspClockPercent = 0;
+ uint64_t dspClockHz = 0;
+ uint32_t latencyInToOut = 0;
+ uint32_t latencyMidiToOut = 0;
+ std::vector<float> preferredSamplerates;
+ std::vector<float> supportedSamplerates;
+
+ baseLib::BinaryStream& write(baseLib::BinaryStream& _s) const override;
+ baseLib::BinaryStream& read(baseLib::BinaryStream& _s) override;
+ };
+
+ struct RequestDeviceState : CommandStruct
+ {
+ synthLib::StateType type;
+
+ baseLib::BinaryStream& write(baseLib::BinaryStream& _s) const override;
+ baseLib::BinaryStream& read(baseLib::BinaryStream& _s) override;
+ };
+
+ struct DeviceState : CommandStruct
+ {
+ synthLib::StateType type;
+ std::vector<uint8_t> state;
+
+ baseLib::BinaryStream& write(baseLib::BinaryStream& _s) const override;
+ baseLib::BinaryStream& read(baseLib::BinaryStream& _s) override;
+
+ bool isValid() const { return !state.empty(); }
+ };
+
+ struct SetSamplerate : CommandStruct
+ {
+ float samplerate;
+
+ baseLib::BinaryStream& write(baseLib::BinaryStream& _s) const override;
+ baseLib::BinaryStream& read(baseLib::BinaryStream& _s) override;
+ };
+
+ struct SetUnknownCustomData : CommandStruct
+ {
+ std::vector<uint8_t> data;
+
+ baseLib::BinaryStream& write(baseLib::BinaryStream& _s) const override;
+ baseLib::BinaryStream& read(baseLib::BinaryStream& _s) override;
+ };
+
+ struct SetDspClockPercent : CommandStruct
+ {
+ uint32_t percent;
+
+ baseLib::BinaryStream& write(baseLib::BinaryStream& _s) const override;
+ baseLib::BinaryStream& read(baseLib::BinaryStream& _s) override;
+ };
+}
diff --git a/source/bridge/bridgeLib/error.cpp b/source/bridge/bridgeLib/error.cpp
diff --git a/source/bridge/bridgeLib/error.h b/source/bridge/bridgeLib/error.h
@@ -0,0 +1,21 @@
+#pragma once
+
+#include <cstdint>
+
+namespace bridgeLib
+{
+ enum class ErrorCode : uint32_t
+ {
+ Ok = 0,
+ NoError = Ok,
+ Unknown,
+
+ WrongProtocolVersion,
+ WrongPluginVersion,
+ InvalidPluginDesc,
+
+ UnexpectedCommand,
+
+ FailedToCreateDevice,
+ };
+}
diff --git a/source/bridge/bridgeLib/pluginDesc.cpp b/source/bridge/bridgeLib/pluginDesc.cpp
@@ -0,0 +1,9 @@
+#include "pluginDesc.h"
+
+baseLib::BinaryStream& bridgeLib::PluginDesc::write(baseLib::BinaryStream& _s)
+{
+}
+
+baseLib::BinaryStream& bridgeLib::PluginDesc::read(baseLib::BinaryStream& _s)
+{
+}
diff --git a/source/bridge/bridgeLib/pluginDesc.h b/source/bridge/bridgeLib/pluginDesc.h
@@ -0,0 +1,10 @@
+#pragma once
+
+#include <cstdint>
+#include <string>
+
+#include "commandStruct.h"
+
+namespace bridgeLib
+{
+}
diff --git a/source/bridge/bridgeLib/tcpConnection.cpp b/source/bridge/bridgeLib/tcpConnection.cpp
@@ -0,0 +1,218 @@
+#include "tcpConnection.h"
+
+#include "audioBuffers.h"
+#include "networkLib/exception.h"
+#include "networkLib/logging.h"
+
+#include "synthLib/midiTypes.h"
+
+namespace bridgeLib
+{
+ TcpConnection::TcpConnection(std::unique_ptr<networkLib::TcpStream>&& _stream) : CommandReader(nullptr), m_stream(std::move(_stream))
+ {
+ m_audioTransferBuffer.reserve(16384);
+
+ start();
+ }
+
+ TcpConnection::~TcpConnection()
+ {
+ shutdown();
+ }
+
+ void TcpConnection::handleCommand(const Command _command, baseLib::BinaryStream& _in)
+ {
+ switch (_command)
+ {
+ case Command::Invalid:
+ case Command::Ping:
+ case Command::Pong:
+ break;
+ case Command::PluginInfo: handleStruct<PluginDesc>(_in); break;
+ case Command::ServerInfo: handleStruct<ServerInfo>(_in); break;
+ case Command::Error: handleStruct<Error>(_in); break;
+ case Command::DeviceInfo: handleDeviceInfo(_in); break;
+ case Command::Midi: handleMidi(_in); break;
+ case Command::Audio: handleAudio(_in); break;
+ case Command::DeviceState: handleDeviceState(_in); break;
+ case Command::RequestDeviceState: handleRequestDeviceState(_in); break;
+ case Command::DeviceCreateParams: handleStruct<DeviceCreateParams>(_in); break;
+ case Command::SetSamplerate: handleStruct<SetSamplerate>(_in); break;
+ case Command::SetDspClockPercent: handleStruct<SetDspClockPercent>(_in); break;
+ case Command::SetUnknownCustomData: handleStruct<SetUnknownCustomData>(_in); break;
+ }
+ }
+
+ void TcpConnection::threadFunc()
+ {
+ try
+ {
+ NetworkThread::threadFunc();
+ }
+ catch (const networkLib::NetException& e)
+ {
+ m_stream->close();
+ LOGNET(networkLib::LogLevel::Warning, "Network Exception, code " << e.type() << ": " << e.what());
+ handleException(e);
+ }
+ }
+
+ void TcpConnection::threadLoopFunc()
+ {
+ read(*m_stream);
+ }
+
+ void TcpConnection::send(const Command _command, const CommandStruct& _data)
+ {
+ m_writer.build(_command, _data);
+ m_writer.write(*m_stream);
+ }
+
+ void TcpConnection::send(Command _command)
+ {
+ m_writer.build(_command);
+ m_writer.write(*m_stream);
+ }
+
+ void TcpConnection::handleMidi(baseLib::BinaryStream& _in)
+ {
+ synthLib::SMidiEvent& ev = m_midiEvent;
+ _in.read(ev.a);
+ _in.read(ev.b);
+ _in.read(ev.c);
+ _in.read(ev.sysex);
+ _in.read(ev.offset);
+ ev.source = static_cast<synthLib::MidiEventSource>(_in.read<uint8_t>());
+ handleMidi(ev);
+ }
+
+ void TcpConnection::sendAudio(const float* const* _data, const uint32_t _numChannels, const uint32_t _numSamplesPerChannel)
+ {
+ auto& s = m_writer.build(Command::Audio);
+ s.write(static_cast<uint8_t>(_numChannels));
+ s.write(_numSamplesPerChannel);
+
+ for(uint32_t i=0; i<_numChannels; ++i)
+ {
+ // if a channel has data, write it. If not, write 0 for the length
+ if(const float* channelData = _data[i])
+ {
+ s.write(_numSamplesPerChannel);
+ s.write(channelData, _numSamplesPerChannel);
+ }
+ else
+ {
+ s.write(0);
+ }
+ }
+ send();
+ }
+
+ void TcpConnection::sendAudio(AudioBuffers& _buffers, const uint32_t _numChannels, uint32_t _numSamplesPerChannel)
+ {
+ auto& s = m_writer.build(Command::Audio);
+ s.write(static_cast<uint8_t>(_numChannels));
+ s.write(_numSamplesPerChannel);
+
+ if(m_audioTransferBuffer.size() < _numSamplesPerChannel)
+ m_audioTransferBuffer.resize(_numSamplesPerChannel);
+
+ for(uint32_t i=0; i<_numChannels; ++i)
+ {
+ _buffers.readInput(i, m_audioTransferBuffer, _numSamplesPerChannel);
+ s.write(_numSamplesPerChannel);
+ s.write(m_audioTransferBuffer.data(), _numSamplesPerChannel);
+ }
+
+ _buffers.onInputRead(_numSamplesPerChannel);
+
+ send();
+ }
+
+ uint32_t TcpConnection::handleAudio(float* const* _output, baseLib::BinaryStream& _in)
+ {
+ const uint32_t numChannels = _in.read<uint8_t>();
+ const uint32_t numSamplesMax = _in.read<uint32_t>();
+
+ for(uint32_t i=0; i<numChannels; ++i)
+ {
+ const auto numSamples = _in.read<uint32_t>();
+ if(numSamples)
+ {
+ assert(_output[i]);
+ _in.read(_output[i], numSamples);
+ }
+ }
+ return numSamplesMax;
+ }
+
+ void TcpConnection::handleAudio(AudioBuffers& _buffers, baseLib::BinaryStream& _in)
+ {
+ const uint32_t numChannels = _in.read<uint8_t>();
+ const uint32_t numSamplesMax = _in.read<uint32_t>();
+
+ for(uint32_t i=0; i<numChannels; ++i)
+ {
+ const auto numSamples = _in.read<uint32_t>();
+
+ if(numSamples)
+ {
+ if(m_audioTransferBuffer.size() < numSamples)
+ m_audioTransferBuffer.resize(numSamples);
+ _in.read(m_audioTransferBuffer.data(), numSamples);
+ _buffers.writeOutput(i, m_audioTransferBuffer, numSamples);
+ }
+ }
+ _buffers.onOutputWritten(numSamplesMax);
+ }
+
+ void TcpConnection::handleAudio(baseLib::BinaryStream& _in)
+ {
+ }
+
+ void TcpConnection::handleRequestDeviceState(baseLib::BinaryStream& _in)
+ {
+ RequestDeviceState requestDeviceState;
+ requestDeviceState.read(_in);
+ handleRequestDeviceState(requestDeviceState);
+ }
+
+ void TcpConnection::handleDeviceState(baseLib::BinaryStream& _in)
+ {
+ m_deviceState.read(_in);
+ handleDeviceState(m_deviceState);
+ }
+
+ void TcpConnection::handleDeviceInfo(baseLib::BinaryStream& _in)
+ {
+ handleStruct<DeviceDesc>(_in);
+ }
+
+ bool TcpConnection::send(const synthLib::SMidiEvent& _ev)
+ {
+ if(!isValid())
+ return false;
+ auto& bs = m_writer.build(Command::Midi);
+ bs.write(_ev.a);
+ bs.write(_ev.b);
+ bs.write(_ev.c);
+ bs.write(_ev.sysex);
+ bs.write(_ev.offset);
+ bs.write<uint8_t>(static_cast<uint8_t>(_ev.source));
+ send();
+ return true;
+ }
+
+ void TcpConnection::close() const
+ {
+ if(m_stream)
+ m_stream->close();
+ }
+
+ void TcpConnection::shutdown()
+ {
+ close();
+ stop();
+ m_stream.reset();
+ }
+}
diff --git a/source/bridge/bridgeLib/tcpConnection.h b/source/bridge/bridgeLib/tcpConnection.h
@@ -0,0 +1,107 @@
+#pragma once
+
+#include "commandReader.h"
+#include "commandWriter.h"
+
+#include "networkLib/networkThread.h"
+#include "networkLib/tcpStream.h"
+#include "synthLib/deviceTypes.h"
+
+#include "synthLib/midiTypes.h"
+
+namespace synthLib
+{
+ struct SMidiEvent;
+}
+
+namespace networkLib
+{
+ class NetException;
+}
+
+namespace bridgeLib
+{
+ class AudioBuffers;
+
+ class TcpConnection : CommandReader, protected networkLib::NetworkThread
+ {
+ public:
+ TcpConnection(std::unique_ptr<networkLib::TcpStream>&& _stream);
+ ~TcpConnection() override;
+
+ bool isValid() const { return m_stream && m_stream->isValid(); }
+
+ void handleCommand(bridgeLib::Command _command, baseLib::BinaryStream& _in) override;
+ void threadFunc() override;
+ void threadLoopFunc() override;
+
+ auto& writer() { return m_writer; }
+ void send()
+ {
+ m_writer.write(*m_stream);
+ }
+ void send(Command _command, const CommandStruct& _data);
+ void send(Command _command);
+
+ // STRUCTS
+ virtual void handleData(const PluginDesc& _desc) {}
+ virtual void handleData(const ServerInfo& _desc) {}
+ virtual void handleData(const DeviceDesc& _desc) {}
+ virtual void handleData(const DeviceCreateParams& _params) {}
+ virtual void handleData(const SetSamplerate& _params) {}
+ virtual void handleData(const SetDspClockPercent& _params) {}
+ virtual void handleData(const SetUnknownCustomData& _params) {}
+ virtual void handleData(const Error& _error) {}
+
+ virtual void handleDeviceInfo(baseLib::BinaryStream& _in);
+
+ // MIDI
+ bool send(const synthLib::SMidiEvent& _ev);
+ void handleMidi(baseLib::BinaryStream& _in);
+ virtual void handleMidi(const synthLib::SMidiEvent& _e) {}
+
+ // AUDIO
+ void sendAudio(const float* const* _data, uint32_t _numChannels, uint32_t _numSamplesPerChannel);
+ void sendAudio(AudioBuffers& _buffers, uint32_t _numChannels, uint32_t _numSamplesPerChannel);
+ static uint32_t handleAudio(float* const* _output, baseLib::BinaryStream& _in);
+ void handleAudio(AudioBuffers& _buffers, baseLib::BinaryStream& _in);
+ virtual void handleAudio(baseLib::BinaryStream& _in);
+
+ // DEVICE STATE
+ virtual void handleRequestDeviceState(baseLib::BinaryStream& _in);
+ virtual void handleRequestDeviceState(bridgeLib::RequestDeviceState& _requestDeviceState) {}
+ virtual void handleDeviceState(baseLib::BinaryStream& _in);
+ virtual void handleDeviceState(DeviceState& _state) {}
+ const auto& getDeviceState() const { return m_deviceState; }
+ auto& getDeviceState() { return m_deviceState; }
+
+ protected:
+ template<typename T>
+ void handleStruct(baseLib::BinaryStream& _in)
+ {
+ T data;
+ handleStruct(data, _in);
+ }
+
+ template<typename T>
+ void handleStruct(T& _dst, baseLib::BinaryStream& _in)
+ {
+ _dst.read(_in);
+ handleData(_dst);
+ }
+
+ virtual void handleException(const networkLib::NetException& _e) = 0;
+ void close() const;
+ void shutdown();
+
+ private:
+ std::unique_ptr<networkLib::TcpStream> m_stream;
+ CommandWriter m_writer;
+
+ synthLib::SMidiEvent m_midiEvent; // preallocated for receiver
+
+ std::vector<float> m_audioTransferBuffer;
+
+ DeviceState m_deviceState;
+ };
+}
diff --git a/source/bridge/bridgeLib/types.h b/source/bridge/bridgeLib/types.h
@@ -0,0 +1,47 @@
+#pragma once
+
+#include <cstdint>
+
+#include "dsp56kEmu/buildconfig.h"
+
+namespace bridgeLib
+{
+ static constexpr uint32_t g_udpServerPort = 56303;
+ static constexpr uint32_t g_tcpServerPort = 56362;
+
+ static constexpr uint32_t g_protocolVersion = 1'00'03;
+
+ using SessionId = uint64_t;
+
+ enum class Platform
+ {
+ Windows,
+ MacOS,
+ Unix
+ };
+
+ enum class Arch
+ {
+ X86,
+ X64,
+ Aarch64
+ };
+
+#ifdef _WIN32
+ static constexpr Platform g_platform = Platform::Windows;
+#elif defined(__APPLE__)
+ static constexpr Platform g_platform = Platform::MacOS;
+#elif defined(__linux__) || defined(__unix__)
+ static constexpr Platform g_platform = Platform::Unix;
+#endif
+
+#ifdef HAVE_ARM64
+ static constexpr Arch g_arch = Arch::Aarch64;
+#elif defined(HAVE_X86_64)
+ static constexpr Arch g_arch = Arch::X64;
+#elif defined(_M_IX86) || defined(__i386__)
+ static constexpr Arch g_arch = Arch::X86;
+#else
+ static_assert(false, "Unknown architecture");
+#endif
+}
diff --git a/source/bridge/client/CMakeLists.txt b/source/bridge/client/CMakeLists.txt
@@ -0,0 +1,24 @@
+cmake_minimum_required(VERSION 3.10)
+
+project(bridgeClient)
+
+add_library(bridgeClient STATIC)
+
+set(SOURCES
+ deviceConnection.cpp deviceConnection.h
+ export.cpp export.h
+ types.h
+ plugin.h
+ remoteDevice.cpp remoteDevice.h
+ serverList.cpp serverList.h
+ udpClient.cpp udpClient.h
+)
+
+target_sources(bridgeClient PRIVATE ${SOURCES})
+
+source_group("source" FILES ${SOURCES})
+
+target_link_libraries(bridgeClient PUBLIC bridgeLib)
+
+target_include_directories(bridgeClient PUBLIC ../)
+set_property(TARGET bridgeClient PROPERTY FOLDER "Bridge")
diff --git a/source/bridge/client/deviceConnection.cpp b/source/bridge/client/deviceConnection.cpp
@@ -0,0 +1,239 @@
+#include "deviceConnection.h"
+
+#include "remoteDevice.h"
+#include "dsp56kEmu/logging.h"
+#include "networkLib/logging.h"
+
+namespace bridgeClient
+{
+ static constexpr uint32_t g_replyTimeoutSecs = 10;
+
+ DeviceConnection::DeviceConnection(RemoteDevice& _device, std::unique_ptr<networkLib::TcpStream>&& _stream) : TcpConnection(std::move(_stream)), m_device(_device)
+ {
+ m_handleReplyFunc = [](bridgeLib::Command, baseLib::BinaryStream&){};
+
+ // send plugin description and device creation parameters, this will cause the server to either boot the device or ask for the rom if it doesn't have it yet
+ send(bridgeLib::Command::PluginInfo, m_device.getPluginDesc());
+
+ // do not send rom data now but only if the server asks for it
+ sendDeviceCreateParams(false);
+ }
+
+ DeviceConnection::~DeviceConnection()
+ {
+ shutdown();
+ }
+
+ void DeviceConnection::handleCommand(const bridgeLib::Command _command, baseLib::BinaryStream& _in)
+ {
+ switch (_command)
+ {
+ case bridgeLib::Command::RequestRom:
+ sendDeviceCreateParams(true);
+ break;
+ default:
+ TcpConnection::handleCommand(_command, _in);
+ break;
+ }
+ }
+
+ void DeviceConnection::handleData(const bridgeLib::DeviceDesc& _desc)
+ {
+ m_deviceDesc = _desc;
+ m_device.onBootFinished(_desc);
+ }
+
+ void DeviceConnection::handleDeviceInfo(baseLib::BinaryStream& _in)
+ {
+ TcpConnection::handleDeviceInfo(_in);
+ m_handleReplyFunc(bridgeLib::Command::DeviceInfo, _in);
+ }
+
+ void DeviceConnection::handleException(const networkLib::NetException& _e)
+ {
+ m_device.onDisconnect();
+ }
+
+ void DeviceConnection::sendDeviceCreateParams(const bool _sendRom)
+ {
+ bridgeLib::DeviceCreateParams p;
+ p.params = m_device.getDeviceCreateParams();
+ if(!_sendRom)
+ p.params.romData.clear();
+ send(bridgeLib::Command::DeviceCreateParams, p);
+ }
+
+ bool DeviceConnection::processAudio(const synthLib::TAudioInputs& _inputs, const synthLib::TAudioOutputs& _outputs, const uint32_t _size, const uint32_t _latency)
+ {
+ m_audioBuffers.writeInput(_inputs, _size);
+
+ std::unique_lock lock(m_cvWaitMutex);
+
+ const auto haveEnoughOutput = m_audioBuffers.getOutputSize() >= _size;
+
+ m_audioBuffers.setLatency(_latency, haveEnoughOutput ? 0 : _size);
+
+ const auto sendSize = m_audioBuffers.getInputSize();
+
+ if(haveEnoughOutput)
+ {
+ lock.unlock();
+
+ if(sendSize > 0)
+ sendAudio(m_audioBuffers, m_device.getChannelCountIn(), sendSize);
+ m_audioBuffers.readOutput(_outputs, _size);
+ }
+ else
+ {
+ assert(sendSize > 0);
+ sendAudio(m_audioBuffers, m_device.getChannelCountIn(), sendSize);
+
+ m_cvWait.wait_for(lock, std::chrono::seconds(g_replyTimeoutSecs), [this, _size]
+ {
+ return m_audioBuffers.getOutputSize() >= _size;
+ });
+
+ if(m_audioBuffers.getOutputSize() < _size)
+ {
+ LOG("Receive timeout, closing connection");
+ close();
+ return false;
+ }
+
+ m_audioBuffers.readOutput(_outputs, _size);
+ }
+ return true;
+ }
+
+ void DeviceConnection::handleAudio(baseLib::BinaryStream& _in)
+ {
+ {
+ std::unique_lock lock(m_cvWaitMutex);
+ TcpConnection::handleAudio(m_audioBuffers, _in);
+ }
+
+ m_cvWait.notify_one();
+ }
+
+ void DeviceConnection::handleMidi(const synthLib::SMidiEvent& _e)
+ {
+ m_midiOut.push_back(_e);
+ }
+
+ void DeviceConnection::readMidiOut(std::vector<synthLib::SMidiEvent>& _midiOut)
+ {
+ _midiOut.insert(_midiOut.end(), m_midiOut.begin(), m_midiOut.end());
+ m_midiOut.clear();
+ }
+
+ bool DeviceConnection::getDeviceState(std::vector<uint8_t>& _state, synthLib::StateType _type)
+ {
+ return sendAwaitReply([this, _type]
+ {
+ bridgeLib::RequestDeviceState s;
+ s.type = _type;
+ send(bridgeLib::Command::RequestDeviceState, s);
+ }, [&](baseLib::BinaryStream& _in)
+ {
+ const auto& s = TcpConnection::getDeviceState().state;
+ _state.insert(_state.end(), s.begin(), s.end());
+ }, bridgeLib::Command::DeviceState);
+ }
+
+ void DeviceConnection::handleDeviceState(baseLib::BinaryStream& _in)
+ {
+ TcpConnection::handleDeviceState(_in);
+ m_handleReplyFunc(bridgeLib::Command::DeviceState, _in);
+ }
+
+ bool DeviceConnection::setDeviceState(const std::vector<uint8_t>& _state, const synthLib::StateType _type)
+ {
+ if(_state.empty())
+ return false;
+
+ auto& s = TcpConnection::getDeviceState();
+ s.state = _state;
+ s.type = _type;
+
+ sendAwaitReply([&]
+ {
+ send(bridgeLib::Command::DeviceState, s);
+ }, [](baseLib::BinaryStream&)
+ {
+ }, bridgeLib::Command::DeviceInfo);
+ return true;
+ }
+
+ void DeviceConnection::setSamplerate(const float _samplerate)
+ {
+ sendAwaitReply([&]
+ {
+ bridgeLib::SetSamplerate sr;
+ sr.samplerate = _samplerate;
+ send(bridgeLib::Command::SetSamplerate, sr);
+ }, [&](baseLib::BinaryStream&)
+ {
+ }, bridgeLib::Command::DeviceInfo);
+ }
+
+ void DeviceConnection::setStateFromUnknownCustomData(const std::vector<uint8_t>& _state)
+ {
+ sendAwaitReply([&]
+ {
+ bridgeLib::SetUnknownCustomData d;
+ d.data = _state;
+ send(bridgeLib::Command::SetUnknownCustomData, d);
+ }, [&](baseLib::BinaryStream&)
+ {
+ }, bridgeLib::Command::DeviceInfo);
+ }
+
+ void DeviceConnection::setDspClockPercent(const uint32_t _percent)
+ {
+ sendAwaitReply([&]
+ {
+ bridgeLib::SetDspClockPercent p;
+ p.percent = _percent;
+ send(bridgeLib::Command::SetDspClockPercent, p);
+ }, [&](baseLib::BinaryStream&)
+ {
+ }, bridgeLib::Command::DeviceInfo);
+ }
+
+ bool DeviceConnection::sendAwaitReply(const std::function<void()>& _send, const std::function<void(baseLib::BinaryStream&)>& _reply, const bridgeLib::Command _replyCommand)
+ {
+ bool receiveDone = false;
+
+ m_handleReplyFunc = [&](const bridgeLib::Command _command, baseLib::BinaryStream& _in)
+ {
+ if(_command != _replyCommand)
+ return;
+
+ _reply(_in);
+
+ {
+ std::unique_lock lockCv(m_cvWaitMutex);
+ receiveDone = true;
+ }
+ m_cvWait.notify_one();
+ };
+
+ _send();
+
+ std::unique_lock lockCv(m_cvWaitMutex);
+ m_cvWait.wait_for(lockCv, std::chrono::seconds(g_replyTimeoutSecs), [&]
+ {
+ return receiveDone;
+ });
+
+ m_handleReplyFunc = [](bridgeLib::Command, baseLib::BinaryStream&)
+ {
+ };
+
+ if(receiveDone)
+ return true;
+ LOG("Receive timeout, closing connection");
+ close();
+ return false;
+ }
+}
diff --git a/source/bridge/client/deviceConnection.h b/source/bridge/client/deviceConnection.h
@@ -0,0 +1,64 @@
+#pragma once
+
+#include <mutex>
+#include <condition_variable>
+
+#include "bridgeLib/audioBuffers.h"
+#include "bridgeLib/tcpConnection.h"
+
+#include "synthLib/audioTypes.h"
+#include "synthLib/deviceTypes.h"
+
+namespace bridgeClient
+{
+ class RemoteDevice;
+
+ class DeviceConnection : public bridgeLib::TcpConnection
+ {
+ public:
+ DeviceConnection(RemoteDevice& _device, std::unique_ptr<networkLib::TcpStream>&& _stream);
+ ~DeviceConnection() override;
+
+ void handleCommand(bridgeLib::Command _command, baseLib::BinaryStream& _in) override;
+
+ void handleData(const bridgeLib::DeviceDesc& _desc) override;
+ void handleDeviceInfo(baseLib::BinaryStream& _in) override;
+
+ void handleException(const networkLib::NetException& _e) override;
+
+ // INIT
+ void sendDeviceCreateParams(bool _sendRom);
+
+ // AUDIO
+ bool processAudio(const synthLib::TAudioInputs& _inputs, const synthLib::TAudioOutputs& _outputs, uint32_t _size, uint32_t _latency);
+ void handleAudio(baseLib::BinaryStream& _in) override;
+
+ // MIDI
+ void handleMidi(const synthLib::SMidiEvent& _e) override;
+ void readMidiOut(std::vector<synthLib::SMidiEvent>& _midiOut);
+
+ // DEVICE STATE
+ bool getDeviceState(std::vector<uint8_t>& _state, synthLib::StateType _type);
+ void handleDeviceState(baseLib::BinaryStream& _in) override;
+ bool setDeviceState(const std::vector<uint8_t>& _state, synthLib::StateType _type);
+
+ void setSamplerate(float _samplerate);
+ void setStateFromUnknownCustomData(const std::vector<uint8_t>& _state);
+ void setDspClockPercent(uint32_t _percent);
+
+ private:
+ bool sendAwaitReply(const std::function<void()>& _send, const std::function<void(baseLib::BinaryStream&)>& _reply, bridgeLib::Command _replyCommand);
+
+ RemoteDevice& m_device;
+ bridgeLib::DeviceDesc m_deviceDesc;
+
+ std::function<void(bridgeLib::Command, baseLib::BinaryStream&)> m_handleReplyFunc;
+
+ std::mutex m_cvWaitMutex;
+ std::condition_variable m_cvWait;
+
+ std::vector<synthLib::SMidiEvent> m_midiOut;
+
+ bridgeLib::AudioBuffers m_audioBuffers;
+ };
+}
diff --git a/source/bridge/client/export.cpp b/source/bridge/client/export.cpp
@@ -0,0 +1 @@
+#include "export.h"
diff --git a/source/bridge/client/export.h b/source/bridge/client/export.h
@@ -0,0 +1,24 @@
+#pragma once
+
+#include "bridgeLib/commands.h"
+
+#if defined(_WIN32) && (defined(_MSC_VER) || defined(__MINGW32__))
+ #define BRIDGE_CLIENT_API __declspec(dllexport)
+#elif defined(_WIN32) && defined(__GNUC__)
+ #define BRIDGE_CLIENT_API __attribute__((__dllexport__))
+#elif defined(__GNUC__)
+ #define BRIDGE_CLIENT_API __attribute__((__visibility__("default")))
+#endif
+
+namespace synthLib
+{
+ struct DeviceCreateParams;
+ class Device;
+}
+
+extern "C"
+{
+ BRIDGE_CLIENT_API synthLib::Device* bridgeDeviceCreate(const synthLib::DeviceCreateParams&);
+ BRIDGE_CLIENT_API void bridgeDeviceDestroy(const synthLib::Device* _device);
+ BRIDGE_CLIENT_API void bridgeDeviceGetDesc(bridgeLib::PluginDesc& _desc);
+}
diff --git a/source/bridge/client/plugin.h b/source/bridge/client/plugin.h
@@ -0,0 +1,46 @@
+// ReSharper disable CppNonInlineFunctionDefinitionInHeaderFile
+#pragma once
+
+#include "export.h"
+
+#include "bridgeLib/commands.h"
+
+namespace synthLib
+{
+ class Device;
+}
+
+namespace bridgeClient
+{
+ void initPluginDesc(bridgeLib::PluginDesc& _desc)
+ {
+ _desc.pluginName = PluginName;
+ _desc.pluginVersion = PluginVersionMajor * 10000 + PluginVersionMinor * 100 + PluginVersionPatch;
+ _desc.plugin4CC = Plugin4CC;
+ }
+
+ void getBridgeDeviceDesc(bridgeLib::PluginDesc& _desc)
+ {
+ initPluginDesc(_desc);
+ }
+}
+
+synthLib::Device* createBridgeDevice(const synthLib::DeviceCreateParams& _params);
+
+extern "C"
+{
+ BRIDGE_CLIENT_API synthLib::Device* bridgeDeviceCreate(const synthLib::DeviceCreateParams& _params)
+ {
+ return createBridgeDevice(_params);
+ }
+
+ BRIDGE_CLIENT_API void bridgeDeviceDestroy(const synthLib::Device* _device)
+ {
+ delete _device;
+ }
+
+ BRIDGE_CLIENT_API void bridgeDeviceGetDesc(bridgeLib::PluginDesc& _desc)
+ {
+ bridgeClient::getBridgeDeviceDesc(_desc);
+ }
+}
diff --git a/source/bridge/client/remoteDevice.cpp b/source/bridge/client/remoteDevice.cpp
@@ -0,0 +1,288 @@
+#include "remoteDevice.h"
+
+#include "udpClient.h"
+
+#include "bridgeLib/types.h"
+
+#include "dsp56kEmu/logging.h"
+
+#include "networkLib/tcpClient.h"
+#include "networkLib/tcpStream.h"
+
+#include "synthLib/deviceException.h"
+
+#include "deviceConnection.h"
+
+#include <condition_variable>
+
+#include "networkLib/exception.h"
+#include "networkLib/logging.h"
+
+#include "synthLib/os.h"
+
+namespace bridgeClient
+{
+ static constexpr uint32_t g_udpTimeout = 5; // seconds
+ static constexpr uint32_t g_tcpTimeout = 5; // seconds
+
+ RemoteDevice::RemoteDevice(const synthLib::DeviceCreateParams& _params, bridgeLib::PluginDesc&& _desc, const std::string& _host/* = {}*/, uint32_t _port/* = 0*/) : Device(_params), m_pluginDesc(std::move(_desc))
+ {
+ getDeviceCreateParams().romHash = baseLib::MD5(getDeviceCreateParams().romData);
+ getDeviceCreateParams().romName = synthLib::getFilenameWithoutPath(getDeviceCreateParams().romName);
+
+ m_pluginDesc.protocolVersion = bridgeLib::g_protocolVersion;
+ createConnection(_host, _port);
+ }
+
+ RemoteDevice::~RemoteDevice()
+ {
+ m_connection.reset();
+ }
+
+ float RemoteDevice::getSamplerate() const
+ {
+ return m_deviceDesc.samplerate;
+ }
+
+ bool RemoteDevice::isValid() const
+ {
+ return m_valid;
+ }
+
+ bool RemoteDevice::getState(std::vector<uint8_t>& _state, const synthLib::StateType _type)
+ {
+ return safeCall([&]
+ {
+ return m_connection->getDeviceState(_state, _type);
+ }, [&]
+ {
+ // if there is no valid connection anymore attempt to grab the latest state that was sent
+ if(!m_connection)
+ return;
+ auto& state = static_cast<bridgeLib::TcpConnection*>(m_connection.get())->getDeviceState();
+ _state.insert(_state.end(), state.state.begin(), state.state.end());
+ });
+ }
+
+ bool RemoteDevice::setState(const std::vector<uint8_t>& _state, const synthLib::StateType _type)
+ {
+ if(!isValid())
+ return false;
+ return m_connection->setDeviceState(_state, _type);
+ }
+
+ uint32_t RemoteDevice::getChannelCountIn()
+ {
+ return m_deviceDesc.inChannels;
+ }
+
+ uint32_t RemoteDevice::getChannelCountOut()
+ {
+ return m_deviceDesc.outChannels;
+ }
+
+ bool RemoteDevice::setDspClockPercent(const uint32_t _percent)
+ {
+ return safeCall([&]
+ {
+ m_connection->setDspClockPercent(_percent);
+ return true;
+ });
+ }
+
+ uint32_t RemoteDevice::getDspClockPercent() const
+ {
+ return m_deviceDesc.dspClockPercent;
+ }
+
+ uint64_t RemoteDevice::getDspClockHz() const
+ {
+ return m_deviceDesc.dspClockHz;
+ }
+
+ uint32_t RemoteDevice::getInternalLatencyInputToOutput() const
+ {
+ return m_deviceDesc.latencyInToOut;
+ }
+
+ uint32_t RemoteDevice::getInternalLatencyMidiToOutput() const
+ {
+ return m_deviceDesc.latencyMidiToOut;
+ }
+
+ void RemoteDevice::getPreferredSamplerates(std::vector<float>& _dst) const
+ {
+ _dst = m_deviceDesc.preferredSamplerates;
+ }
+
+ void RemoteDevice::getSupportedSamplerates(std::vector<float>& _dst) const
+ {
+ _dst = m_deviceDesc.supportedSamplerates;
+ }
+
+ bool RemoteDevice::setSamplerate(const float _samplerate)
+ {
+ return safeCall([&]
+ {
+ m_connection->setSamplerate(_samplerate);
+ return true;
+ });
+ }
+
+ bool RemoteDevice::setStateFromUnknownCustomData(const std::vector<uint8_t>& _state)
+ {
+ return safeCall([&]
+ {
+ m_connection->setStateFromUnknownCustomData(_state);
+ return true;
+ });
+ }
+
+ void RemoteDevice::onBootFinished(const bridgeLib::DeviceDesc& _desc)
+ {
+ {
+ std::unique_lock lock(m_cvWaitMutex);
+ m_deviceDesc = _desc;
+ }
+ m_cvWait.notify_one();
+ }
+
+ void RemoteDevice::onDisconnect()
+ {
+ {
+ std::unique_lock lock(m_cvWaitMutex);
+ m_valid = false;
+ }
+ m_cvWait.notify_one();
+ }
+
+ void RemoteDevice::readMidiOut(std::vector<synthLib::SMidiEvent>& _midiOut)
+ {
+ safeCall([&]
+ {
+ m_connection->readMidiOut(_midiOut);
+ return true;
+ });
+ }
+
+ void RemoteDevice::processAudio(const synthLib::TAudioInputs& _inputs, const synthLib::TAudioOutputs& _outputs, const size_t _samples)
+ {
+ safeCall([&]
+ {
+ return m_connection->processAudio(_inputs, _outputs, static_cast<uint32_t>(_samples), getExtraLatencySamples());
+ });
+ }
+
+ bool RemoteDevice::sendMidi(const synthLib::SMidiEvent& _ev, std::vector<synthLib::SMidiEvent>& _response)
+ {
+ return safeCall([&]
+ {
+ return m_connection->send(_ev);
+ });
+ }
+
+ bool RemoteDevice::safeCall(const std::function<bool()>& _func, const std::function<void()>& _onFail/* = [] {}*/)
+ {
+ if(!isValid())
+ {
+ _onFail();
+ return false;
+ }
+
+ try
+ {
+ if(!_func())
+ {
+ onDisconnect();
+ _onFail();
+ }
+ }
+ catch(networkLib::NetException& e)
+ {
+ LOG(e.what());
+ onDisconnect();
+ _onFail();
+ }
+ return true;
+ }
+
+ void RemoteDevice::createConnection(const std::string& _host, uint32_t _port)
+ {
+ // wait for UDP connection up to 5 seconds
+ bridgeLib::ServerInfo si;
+ std::string host = _host;
+
+ if(_host.empty() || !_port)
+ {
+ UdpClient udpClient(m_pluginDesc, [&](const std::string& _hostname, const bridgeLib::ServerInfo& _si, const bridgeLib::Error& _err)
+ {
+ if(_err.code != bridgeLib::ErrorCode::Ok)
+ return;
+ {
+ std::unique_lock lock(m_cvWaitMutex);
+ si = _si;
+ host = _hostname;
+ LOG("Found server "<< _hostname << ':' << si.portTcp);
+ }
+ m_cvWait.notify_one();
+ });
+
+ std::unique_lock lockCv(m_cvWaitMutex);
+ if(!m_cvWait.wait_for(lockCv, std::chrono::seconds(g_udpTimeout), [&si]
+ {
+ return si.protocolVersion == bridgeLib::g_protocolVersion;
+ }))
+ {
+ throw synthLib::DeviceException(synthLib::DeviceError::RemoteUdpConnectFailed, "No server found");
+ }
+ }
+ else
+ {
+ si.portTcp = _port;
+ }
+
+ // wait for TCP connection for another 5 seconds
+ std::unique_ptr<networkLib::TcpStream> stream;
+
+ LOG("Connecting to "<< host << ':' << si.portTcp);
+
+ networkLib::TcpClient client(host, si.portTcp,[&](std::unique_ptr<networkLib::TcpStream> _tcpStream)
+ {
+ {
+ std::unique_lock lock(m_cvWaitMutex);
+ stream = std::move(_tcpStream);
+ }
+ m_cvWait.notify_one();
+ });
+
+ {
+ std::unique_lock lockCv(m_cvWaitMutex);
+ if(!m_cvWait.wait_for(lockCv, std::chrono::seconds(g_tcpTimeout), [&stream]
+ {
+ return stream.get() && stream->isValid();
+ }))
+ {
+ throw synthLib::DeviceException(synthLib::DeviceError::RemoteTcpConnectFailed, "Failed to connect to " + host + ':' + std::to_string(si.portTcp));
+ }
+ }
+
+ // we are connected. Wait for device info as long as the connection is alive. The server will either
+ // close it if the requirements are not fulfilled (plugin not existing on server) or will eventually
+ // send device info after the device has bene opened on the server
+ m_connection.reset(new DeviceConnection(*this, std::move(stream)));
+
+ std::unique_lock lockCv(m_cvWaitMutex);
+ m_cvWait.wait(lockCv, [this]()
+ {
+ // continue waiting if the connection is still active but we didn't receive a device desc yet
+ return !m_connection->isValid() || m_deviceDesc.outChannels > 0;
+ });
+
+ if(!m_connection->isValid() || m_deviceDesc.outChannels == 0)
+ throw synthLib::DeviceException(synthLib::DeviceError::RemoteTcpConnectFailed);
+
+ m_valid = true;
+
+ LOG("Connection established successfully");
+ }
+}
diff --git a/source/bridge/client/remoteDevice.h b/source/bridge/client/remoteDevice.h
@@ -0,0 +1,71 @@
+#pragma once
+
+#include <condition_variable>
+#include <functional>
+#include <memory>
+
+#include "bridgeLib/commands.h"
+
+#include "synthLib/device.h"
+
+namespace bridgeLib
+{
+ struct PluginDesc;
+}
+
+namespace bridgeClient
+{
+ class DeviceConnection;
+
+ class RemoteDevice : public synthLib::Device
+ {
+ public:
+ RemoteDevice(const synthLib::DeviceCreateParams& _params, bridgeLib::PluginDesc&& _desc, const std::string& _host = {}, uint32_t _port = 0);
+ ~RemoteDevice() override;
+ RemoteDevice(RemoteDevice&&) = delete;
+ RemoteDevice(const RemoteDevice&) = delete;
+ RemoteDevice& operator = (RemoteDevice&&) = delete;
+ RemoteDevice& operator = (const RemoteDevice&) = delete;
+
+ float getSamplerate() const override;
+ bool isValid() const override;
+ bool getState(std::vector<uint8_t>& _state, synthLib::StateType _type) override;
+ bool setState(const std::vector<uint8_t>& _state, synthLib::StateType _type) override;
+ uint32_t getChannelCountIn() override;
+ uint32_t getChannelCountOut() override;
+ bool setDspClockPercent(uint32_t _percent) override;
+ uint32_t getDspClockPercent() const override;
+ uint64_t getDspClockHz() const override;
+ uint32_t getInternalLatencyInputToOutput() const override;
+ uint32_t getInternalLatencyMidiToOutput() const override;
+ void getPreferredSamplerates(std::vector<float>& _dst) const override;
+ void getSupportedSamplerates(std::vector<float>& _dst) const override;
+
+ const auto& getPluginDesc() const { return m_pluginDesc; }
+ auto& getPluginDesc() { return m_pluginDesc; }
+
+ bool setSamplerate(float _samplerate) override;
+
+ bool setStateFromUnknownCustomData(const std::vector<uint8_t>& _state) override;
+
+ void onBootFinished(const bridgeLib::DeviceDesc& _desc);
+ void onDisconnect();
+
+ protected:
+ void readMidiOut(std::vector<synthLib::SMidiEvent>& _midiOut) override;
+ void processAudio(const synthLib::TAudioInputs& _inputs, const synthLib::TAudioOutputs& _outputs, size_t _samples) override;
+ bool sendMidi(const synthLib::SMidiEvent& _ev, std::vector<synthLib::SMidiEvent>& _response) override;
+
+ bool safeCall(const std::function<bool()>& _func, const std::function<void()>& _onFail = [] {});
+
+ void createConnection(const std::string& _host, uint32_t _port);
+
+ bridgeLib::PluginDesc m_pluginDesc;
+ bridgeLib::DeviceDesc m_deviceDesc;
+ std::unique_ptr<DeviceConnection> m_connection;
+
+ std::mutex m_cvWaitMutex;
+ std::condition_variable m_cvWait;
+ bool m_valid = false;
+ };
+}
diff --git a/source/bridge/client/serverList.cpp b/source/bridge/client/serverList.cpp
@@ -0,0 +1,53 @@
+#include "serverList.h"
+
+namespace bridgeClient
+{
+ constexpr std::chrono::seconds g_expireTime(10);
+
+ ServerList::ServerList(const bridgeLib::PluginDesc& _desc) : m_udpClient(_desc, [this](const std::string& _host, const bridgeLib::ServerInfo& _serverInfo, const bridgeLib::Error& _error)
+ {
+ onServerFound(_host, _serverInfo, _error);
+ })
+ {
+ }
+
+ std::set<ServerList::Entry> ServerList::getEntries() const
+ {
+ std::set<Entry> entries;
+ {
+ std::scoped_lock lock(m_entriesMutex);
+ entries = m_entries;
+ }
+ removeExpiredEntries(entries);
+ return entries;
+ }
+
+ void ServerList::removeExpiredEntries(std::set<Entry>& _entries)
+ {
+ for(auto it = _entries.begin(); it != _entries.end();)
+ {
+ auto& e = *it;
+
+ const auto elapsed = std::chrono::duration_cast<std::chrono::seconds>(Clock::now() - e.lastSeen);
+
+ if(elapsed >= g_expireTime)
+ it = _entries.erase(it);
+ else
+ ++it;
+ }
+ }
+
+ void ServerList::onServerFound(const std::string& _host, const bridgeLib::ServerInfo& _serverInfo, const bridgeLib::Error& _error)
+ {
+ Entry e;
+ e.serverInfo = _serverInfo;
+ e.err = _error;
+ e.host = _host;
+ e.lastSeen = std::chrono::system_clock::now();
+
+ std::scoped_lock lock(m_entriesMutex);
+ m_entries.erase(e);
+ removeExpiredEntries(m_entries);
+ m_entries.insert(e);
+ }
+}
diff --git a/source/bridge/client/serverList.h b/source/bridge/client/serverList.h
@@ -0,0 +1,52 @@
+#pragma once
+
+#include <mutex>
+#include <set>
+#include <string>
+#include <chrono>
+
+#include "udpClient.h"
+
+#include "bridgeLib/commands.h"
+
+namespace bridgeClient
+{
+ class ServerList
+ {
+ public:
+ using Clock = std::chrono::system_clock;
+ using Timestamp = Clock::time_point;
+
+ struct Entry
+ {
+ std::string host;
+ bridgeLib::ServerInfo serverInfo;
+ bridgeLib::Error err;
+ Timestamp lastSeen;
+
+ bool operator < (const Entry& _e) const
+ {
+ return host < _e.host;
+ }
+
+ bool operator == (const Entry& _e) const
+ {
+ return serverInfo.portTcp == _e.serverInfo.portTcp && host == _e.host;
+ }
+ };
+
+ ServerList(const bridgeLib::PluginDesc& _desc);
+
+ std::set<Entry> getEntries() const;
+
+ static void removeExpiredEntries(std::set<Entry>& _entries);
+
+ private:
+ void onServerFound(const std::string& _host, const bridgeLib::ServerInfo& _serverInfo, const bridgeLib::Error& _error);
+
+ UdpClient m_udpClient;
+
+ mutable std::mutex m_entriesMutex;
+ std::set<Entry> m_entries;
+ };
+}
diff --git a/source/bridge/client/types.h b/source/bridge/client/types.h
@@ -0,0 +1 @@
+#pragma once
diff --git a/source/bridge/client/udpClient.cpp b/source/bridge/client/udpClient.cpp
@@ -0,0 +1,65 @@
+#include "udpClient.h"
+
+#include <utility>
+
+#include "bridgeLib/commandWriter.h"
+#include "bridgeLib/error.h"
+#include "bridgeLib/types.h"
+
+namespace bridgeClient
+{
+ UdpClient::UdpClient(const bridgeLib::PluginDesc& _desc, ServerFoundCallback&& _callback)
+ : networkLib::UdpClient(bridgeLib::g_udpServerPort)
+ , m_callback(std::move(_callback))
+ {
+ bridgeLib::PluginDesc desc = _desc;
+
+ desc.protocolVersion = bridgeLib::g_protocolVersion;
+
+ bridgeLib::CommandWriter w;
+ desc.write(w.build(bridgeLib::Command::PluginInfo));
+
+ baseLib::BinaryStream bs;
+ w.write(bs);
+ bs.toVector(m_requestPacket);
+
+ start();
+ }
+
+ bool UdpClient::validateResponse(const std::string& _host, const std::vector<uint8_t>& _message)
+ {
+ bool ok = false;
+
+ bridgeLib::CommandReader reader([&](const bridgeLib::Command _command, baseLib::BinaryStream& _binaryStream)
+ {
+ if(_command == bridgeLib::Command::ServerInfo)
+ {
+ bridgeLib::ServerInfo si;
+ si.read(_binaryStream);
+
+ if(si.protocolVersion == bridgeLib::g_protocolVersion && si.portTcp > 0)
+ {
+ ok = true;
+ m_callback(_host, si, {});
+ }
+ else
+ {
+ bridgeLib::Error e;
+ e.code = bridgeLib::ErrorCode::WrongProtocolVersion;
+ e.msg = "Wrong protocol version";
+ m_callback(_host, si, e);
+ }
+ }
+ else if(_command == bridgeLib::Command::Error)
+ {
+ bridgeLib::Error e;
+ e.read(_binaryStream);
+ m_callback(_host, {}, e);
+ }
+ });
+
+ baseLib::BinaryStream bs(_message);
+ reader.read(bs);
+ return false; // continue
+ }
+}
diff --git a/source/bridge/client/udpClient.h b/source/bridge/client/udpClient.h
@@ -0,0 +1,26 @@
+#pragma once
+
+#include "bridgeLib/commandReader.h"
+
+#include "networkLib/udpClient.h"
+
+namespace bridgeClient
+{
+ class UdpClient : networkLib::UdpClient
+ {
+ public:
+ using ServerFoundCallback = std::function<void(const std::string&, const bridgeLib::ServerInfo&, const bridgeLib::Error&)>;
+
+ UdpClient(const bridgeLib::PluginDesc& _desc, ServerFoundCallback&& _callback);
+
+ private:
+ bool validateResponse(const std::string& _host, const std::vector<uint8_t>& _message) override;
+ const std::vector<uint8_t>& getRequestPacket() override
+ {
+ return m_requestPacket;
+ }
+
+ std::vector<uint8_t> m_requestPacket;
+ ServerFoundCallback m_callback;
+ };
+}
diff --git a/source/bridge/server/CMakeLists.txt b/source/bridge/server/CMakeLists.txt
@@ -0,0 +1,26 @@
+cmake_minimum_required(VERSION 3.10)
+
+project(bridgeServer)
+
+add_executable(bridgeServer)
+
+set(SOURCES
+ bridgeServer.cpp
+ clientConnection.cpp clientConnection.h
+ config.cpp config.h
+ server.cpp server.h
+ import.cpp import.h
+ romPool.cpp romPool.h
+ udpServer.cpp udpServer.h
+)
+
+target_sources(bridgeServer PRIVATE ${SOURCES})
+
+source_group("source" FILES ${SOURCES})
+
+target_link_libraries(bridgeServer PUBLIC bridgeLib)
+
+set_property(TARGET bridgeServer PROPERTY FOLDER "Bridge")
+set_property(TARGET bridgeServer PROPERTY OUTPUT_NAME "dsp56300EmuServer")
+
+install(TARGETS bridgeServer DESTINATION . COMPONENT DSPBridgeServer)
diff --git a/source/bridge/server/bridgeServer.cpp b/source/bridge/server/bridgeServer.cpp
@@ -0,0 +1,43 @@
+#include <iostream>
+
+#include "server.h"
+
+#ifndef _WIN32
+#include <cstdio>
+#include <execinfo.h>
+#include <signal.h>
+#include <cstdlib>
+
+void segFaultHandler(int sig)
+{
+ void *array[10];
+ size_t size;
+
+ size = backtrace(array, 10);
+
+ fprintf(stderr, "Error: signal %d:\n", sig);
+ backtrace_symbols_fd(array, size, 2);
+ exit(1);
+}
+#endif
+
+int main(int _argc, char** _argv)
+{
+#ifndef _WIN32
+ signal(SIGSEGV, segFaultHandler);
+#endif
+
+ while(true)
+ {
+ try
+ {
+ bridgeServer::Server server(_argc, _argv);
+ server.run();
+ }
+ catch(...)
+ {
+ std::cout << "Server exception, attempting to restart\n";
+ }
+ }
+ return 0;
+}
diff --git a/source/bridge/server/clientConnection.cpp b/source/bridge/server/clientConnection.cpp
@@ -0,0 +1,290 @@
+#include "clientConnection.h"
+
+#include "server.h"
+#include "bridgeLib/error.h"
+#include "networkLib/logging.h"
+
+namespace bridgeServer
+{
+ static constexpr uint32_t g_audioBufferSize = 16384;
+
+ ClientConnection::ClientConnection(Server& _server, std::unique_ptr<networkLib::TcpStream>&& _stream, std::string _name)
+ : TcpConnection(std::move(_stream))
+ , m_server(_server)
+ , m_name(std::move(_name))
+ {
+ for(size_t i=0; i<m_audioInputBuffers.size(); ++i)
+ {
+ auto& buf = m_audioInputBuffers[i];
+ buf.resize(g_audioBufferSize);
+ m_audioInputs[i] = buf.data();
+ }
+
+ for(size_t i=0; i<m_audioOutputBuffers.size(); ++i)
+ {
+ auto& buf = m_audioOutputBuffers[i];
+ buf.resize(g_audioBufferSize);
+ m_audioOutputs[i] = buf.data();
+ }
+
+ m_midiIn.reserve(1024);
+ m_midiOut.reserve(4096);
+
+ getDeviceState().state.reserve(8 * 1024 * 1024);
+ }
+
+ ClientConnection::~ClientConnection()
+ {
+ shutdown();
+ destroyDevice();
+ }
+
+ void ClientConnection::handleMidi(const synthLib::SMidiEvent& _e)
+ {
+ m_midiIn.push_back(_e);
+ }
+
+ void ClientConnection::handleData(const bridgeLib::PluginDesc& _desc)
+ {
+ m_pluginDesc = _desc;
+ LOGNET(networkLib::LogLevel::Info, "Client " << m_name << " identified as plugin " << _desc.pluginName << ", version " << _desc.pluginVersion);
+ m_name = m_pluginDesc.pluginName + '-' + m_name;
+ createDevice();
+ }
+
+ void ClientConnection::handleData(const bridgeLib::DeviceCreateParams& _params)
+ {
+ const auto& p = _params.params;
+
+ if(p.romData.empty())
+ {
+ // if no rom data has been transmitted, try to load from cache
+ const auto& romData = m_server.getRomPool().getRom(p.romHash);
+
+ if(!romData.empty())
+ {
+ // if we have the ROM, we are good to go
+ LOGNET(networkLib::LogLevel::Info, "ROM " << p.romName << " with hash " << p.romHash.toString() << " found, transfer skipped");
+
+ m_deviceCreateParams = p;
+ m_deviceCreateParams.romData = romData;
+
+ createDevice();
+ }
+ else
+ {
+ if(m_romRequested)
+ {
+ LOGNET(networkLib::LogLevel::Warning, "Client " << m_name << " failed to provide rom that we asked for, disconnecting");
+ close();
+ return;
+ }
+
+ // If not, ask client to send it
+ send(bridgeLib::Command::RequestRom);
+ LOGNET(networkLib::LogLevel::Info, "ROM " << p.romName << " with hash " << p.romHash.toString() << " not found, requesting client to send it");
+ m_romRequested = true;
+ }
+ }
+ else
+ {
+ // client sent the rom. Validate transmission by comparing hashes
+ const baseLib::MD5 calculatedHash(p.romData);
+ if(calculatedHash != p.romHash)
+ {
+ LOGNET(networkLib::LogLevel::Error, "Calculated hash " << calculatedHash.toString() << " of ROM " << p.romName << " does not match sent hash " << p.romHash.toString() << ", transfer error");
+ close();
+ }
+
+ LOGNET(networkLib::LogLevel::Info, "Adding ROM " << p.romName << " with hash " << p.romHash.toString() << " to pool");
+ m_server.getRomPool().addRom(p.romName, p.romData);
+
+ m_deviceCreateParams = p;
+
+ createDevice();
+ }
+ }
+
+ void ClientConnection::handleAudio(baseLib::BinaryStream& _in)
+ {
+ if(!m_device)
+ {
+ errorClose(bridgeLib::ErrorCode::UnexpectedCommand, "Audio data without valid device");
+ return;
+ }
+
+ const auto numSamples = TcpConnection::handleAudio(const_cast<float* const*>(m_audioInputs.data()), _in);
+
+ m_device->process(m_audioInputs, m_audioOutputs, numSamples, m_midiIn, m_midiOut);
+
+ for (const auto& midiOut : m_midiOut)
+ send(midiOut);
+
+ sendAudio(m_audioOutputs.data(), std::min(static_cast<uint32_t>(m_audioOutputs.size()), m_device->getChannelCountOut()), numSamples);
+
+ m_midiIn.clear();
+
+ m_device->release(m_midiOut);
+ }
+
+ void ClientConnection::sendDeviceState(const synthLib::StateType _type)
+ {
+ if(!m_device)
+ return;
+
+ std::scoped_lock lock(m_mutexDeviceState);
+ auto& state = getDeviceState();
+ state.type = _type;
+
+ state.state.clear();
+ m_device->getState(state.state, _type);
+
+ send(bridgeLib::Command::DeviceState, state);
+ }
+
+ void ClientConnection::handleRequestDeviceState(bridgeLib::RequestDeviceState& _requestDeviceState)
+ {
+ if(!m_device)
+ {
+ errorClose(bridgeLib::ErrorCode::UnexpectedCommand, "Device state request without valid device");
+ return;
+ }
+
+ sendDeviceState(_requestDeviceState.type);
+ }
+
+ void ClientConnection::handleDeviceState(bridgeLib::DeviceState& _in)
+ {
+ if(!m_device)
+ {
+ errorClose(bridgeLib::ErrorCode::UnexpectedCommand, "Device state without valid device");
+ return;
+ }
+
+ m_device->setState(_in.state, _in.type);
+ sendDeviceInfo();
+ }
+
+ void ClientConnection::handleDeviceState(baseLib::BinaryStream& _in)
+ {
+ std::scoped_lock lock(m_mutexDeviceState);
+ TcpConnection::handleDeviceState(_in);
+ }
+
+ void ClientConnection::handleException(const networkLib::NetException& _e)
+ {
+ exit(true);
+ m_server.onClientException(*this, _e);
+ }
+
+ void ClientConnection::handleData(const bridgeLib::SetSamplerate& _params)
+ {
+ if(!m_device)
+ {
+ errorClose(bridgeLib::ErrorCode::UnexpectedCommand, "Set samplerate request without valid device");
+ return;
+ }
+
+ m_device->setSamplerate(_params.samplerate);
+ sendDeviceInfo();
+ }
+
+ void ClientConnection::handleData(const bridgeLib::SetDspClockPercent& _params)
+ {
+ if(!m_device)
+ {
+ errorClose(bridgeLib::ErrorCode::UnexpectedCommand, "Set DSP clock request without valid device");
+ return;
+ }
+
+ m_device->setDspClockPercent(_params.percent);
+ sendDeviceInfo();
+ }
+
+ void ClientConnection::handleData(const bridgeLib::SetUnknownCustomData& _params)
+ {
+ if(!m_device)
+ {
+ errorClose(bridgeLib::ErrorCode::UnexpectedCommand, "Set custom data request without valid device");
+ return;
+ }
+
+ m_device->setStateFromUnknownCustomData(_params.data);
+ sendDeviceInfo();
+ }
+
+ void ClientConnection::sendDeviceInfo()
+ {
+ bridgeLib::DeviceDesc deviceDesc;
+
+ deviceDesc.samplerate = m_device->getSamplerate();
+ deviceDesc.outChannels = m_device->getChannelCountOut();
+ deviceDesc.inChannels = m_device->getChannelCountIn();
+ deviceDesc.dspClockPercent = m_device->getDspClockPercent();
+ deviceDesc.dspClockHz = m_device->getDspClockHz();
+ deviceDesc.latencyInToOut = m_device->getInternalLatencyInputToOutput();
+ deviceDesc.latencyMidiToOut = m_device->getInternalLatencyMidiToOutput();
+
+ deviceDesc.preferredSamplerates.reserve(64);
+ deviceDesc.supportedSamplerates.reserve(64);
+
+ m_device->getPreferredSamplerates(deviceDesc.preferredSamplerates);
+ m_device->getSupportedSamplerates(deviceDesc.supportedSamplerates);
+
+ send(bridgeLib::Command::DeviceInfo, deviceDesc);
+ }
+
+ void ClientConnection::createDevice()
+ {
+ if(m_pluginDesc.pluginVersion == 0 || m_deviceCreateParams.romData.empty())
+ return;
+
+ m_device = m_server.getPlugins().createDevice(m_deviceCreateParams, m_pluginDesc);
+
+ if(!m_device)
+ {
+ errorClose(bridgeLib::ErrorCode::FailedToCreateDevice,"Failed to create device");
+ return;
+ }
+
+ const auto& d = m_pluginDesc;
+ LOGNET(networkLib::LogLevel::Info, "Created new device for plugin '" << d.pluginName << "', version " << d.pluginVersion << ", id " << d.plugin4CC);
+
+ // recover a previously lost connection if possible
+ const auto cachedDeviceState = m_server.getCachedDeviceState(d.sessionId);
+
+ if(cachedDeviceState.isValid())
+ {
+ LOGNET(networkLib::LogLevel::Info, m_name << ": Recovering previous device state for session id " << d.sessionId);
+ send(bridgeLib::Command::DeviceState, cachedDeviceState);
+ m_device->setState(cachedDeviceState.state, cachedDeviceState.type);
+ }
+
+ sendDeviceInfo();
+ }
+
+ void ClientConnection::destroyDevice()
+ {
+ if(!m_device)
+ return;
+
+ if(isValid())
+ sendDeviceState(synthLib::StateTypeGlobal);
+
+ m_server.getPlugins().destroyDevice(m_pluginDesc, m_device);
+ m_device = nullptr;
+ }
+
+ void ClientConnection::errorClose(const bridgeLib::ErrorCode _code, const std::string& _err)
+ {
+ LOGNET(networkLib::LogLevel::Error, m_name + ": " + _err);
+
+ bridgeLib::Error err;
+ err.code = _code;
+ err.msg = _err;
+
+ send(bridgeLib::Command::Error, err);
+ std::this_thread::sleep_for(std::chrono::seconds(1));
+ close();
+ }
+}
diff --git a/source/bridge/server/clientConnection.h b/source/bridge/server/clientConnection.h
@@ -0,0 +1,63 @@
+#pragma once
+
+#include <mutex>
+
+#include "bridgeLib/tcpConnection.h"
+#include "networkLib/networkThread.h"
+#include "networkLib/tcpStream.h"
+#include "synthLib/device.h"
+
+namespace bridgeServer
+{
+ class Server;
+
+ class ClientConnection : public bridgeLib::TcpConnection
+ {
+ public:
+ ClientConnection(Server& _server, std::unique_ptr<networkLib::TcpStream>&& _stream, std::string _name);
+ ~ClientConnection() override;
+
+ void handleMidi(const synthLib::SMidiEvent& _e) override;
+ void handleData(const bridgeLib::PluginDesc& _desc) override;
+ void handleData(const bridgeLib::DeviceCreateParams& _params) override;
+ void handleData(const bridgeLib::SetSamplerate& _params) override;
+ void handleData(const bridgeLib::SetDspClockPercent& _params) override;
+ void handleData(const bridgeLib::SetUnknownCustomData& _params) override;
+
+ void handleAudio(baseLib::BinaryStream& _in) override;
+ void sendDeviceState(synthLib::StateType _type);
+ void handleRequestDeviceState(bridgeLib::RequestDeviceState& _requestDeviceState) override;
+ void handleDeviceState(bridgeLib::DeviceState& _in) override;
+ void handleDeviceState(baseLib::BinaryStream& _in) override;
+ void handleException(const networkLib::NetException& _e) override;
+
+ const auto& getPluginDesc() const { return m_pluginDesc; }
+
+ private:
+ void sendDeviceInfo();
+ void createDevice();
+ void destroyDevice();
+
+ void errorClose(bridgeLib::ErrorCode _code, const std::string& _err);
+
+ Server& m_server;
+ std::string m_name;
+
+ bridgeLib::PluginDesc m_pluginDesc;
+ synthLib::DeviceCreateParams m_deviceCreateParams;
+
+ synthLib::Device* m_device = nullptr;
+
+ synthLib::TAudioInputs m_audioInputs;
+ synthLib::TAudioOutputs m_audioOutputs;
+
+ std::array<std::vector<float>, std::tuple_size_v<synthLib::TAudioInputs>> m_audioInputBuffers;
+ std::array<std::vector<float>, std::tuple_size_v<synthLib::TAudioOutputs>> m_audioOutputBuffers;
+ std::vector<synthLib::SMidiEvent> m_midiIn;
+ std::vector<synthLib::SMidiEvent> m_midiOut;
+
+ bool m_romRequested = false;
+
+ std::mutex m_mutexDeviceState;
+ };
+}
diff --git a/source/bridge/server/config.cpp b/source/bridge/server/config.cpp
@@ -0,0 +1,43 @@
+#include "config.h"
+
+#include "server.h"
+#include "baseLib/commandline.h"
+#include "baseLib/configFile.h"
+
+#include "synthLib/os.h"
+
+namespace bridgeServer
+{
+ Config::Config(int _argc, char** _argv)
+ : portTcp(bridgeLib::g_tcpServerPort)
+ , portUdp(bridgeLib::g_udpServerPort)
+ , deviceStateRefreshMinutes(3)
+ , pluginsPath(getDefaultDataPath() + "plugins/")
+ , romsPath(getDefaultDataPath() + "roms/")
+ {
+ const baseLib::CommandLine commandLine(_argc, _argv);
+
+ auto configFilename = commandLine.get("config");
+
+ if (configFilename.empty())
+ configFilename = getDefaultDataPath() + "config/dspBridgeServer.cfg";
+
+ baseLib::ConfigFile config(configFilename);
+
+ config.add(commandLine, true);
+
+ portTcp = config.getInt("tcpPort", static_cast<int>(portTcp));
+ portUdp = config.getInt("tcpPort", static_cast<int>(portUdp));
+ deviceStateRefreshMinutes = config.getInt("deviceStateRefreshMinutes", static_cast<int>(deviceStateRefreshMinutes));
+ pluginsPath = config.get("pluginsPath", pluginsPath);
+ romsPath = config.get("romsPath", romsPath);
+
+ synthLib::createDirectory(pluginsPath);
+ synthLib::createDirectory(romsPath);
+ }
+
+ std::string Config::getDefaultDataPath()
+ {
+ return synthLib::validatePath(synthLib::getSpecialFolderPath(synthLib::SpecialFolderType::UserDocuments)) + "The Usual Suspects/dspBridgeServer/";
+ }
+}
diff --git a/source/bridge/server/config.h b/source/bridge/server/config.h
@@ -0,0 +1,20 @@
+#pragma once
+
+#include <cstdint>
+#include <string>
+
+namespace bridgeServer
+{
+ struct Config
+ {
+ Config(int _argc, char** _argv);
+
+ uint32_t portTcp;
+ uint32_t portUdp;
+ uint32_t deviceStateRefreshMinutes;
+ std::string pluginsPath;
+ std::string romsPath;
+
+ static std::string getDefaultDataPath();
+ };
+}
diff --git a/source/bridge/server/import.cpp b/source/bridge/server/import.cpp
@@ -0,0 +1,156 @@
+#include "import.h"
+
+#include "config.h"
+#include "networkLib/logging.h"
+#include "synthLib/deviceException.h"
+#include "synthLib/os.h"
+
+#ifdef _WIN32
+#define NOMINMAX
+#include <Windows.h>
+#define RTLD_LAZY 0
+void* dlopen (const char* _filename, int)
+{
+ return LoadLibraryA(_filename);
+}
+FARPROC dlsym (void* _handle, const char* _name)
+{
+ return GetProcAddress (static_cast<HMODULE>(_handle), _name);
+}
+int dlclose(void* _handle)
+{
+ return FreeLibrary(static_cast<HMODULE>(_handle));
+}
+#else
+#include <dlfcn.h>
+#endif
+
+namespace bridgeServer
+{
+ Import::Import(const Config& _config) : m_config(_config)
+ {
+ findPlugins();
+ }
+
+ Import::~Import()
+ {
+ for (const auto& it : m_loadedPlugins)
+ dlclose(it.second.handle);
+ m_loadedPlugins.clear();
+ }
+
+ synthLib::Device* Import::createDevice(const synthLib::DeviceCreateParams& _params, const bridgeLib::PluginDesc& _desc)
+ {
+ std::scoped_lock lock(m_mutex);
+
+ auto it = m_loadedPlugins.find(_desc);
+
+ if(it == m_loadedPlugins.end())
+ findPlugins(); // try to load additional plugins if not found
+
+ it = m_loadedPlugins.find(_desc);
+ if(it == m_loadedPlugins.end())
+ {
+ LOGNET(networkLib::LogLevel::Warning, "Failed to create device for plugin '" << _desc.pluginName << "', version " << _desc.pluginVersion << ", id " << _desc.plugin4CC << ", no matching plugin available");
+ return nullptr; // still not found
+ }
+
+ try
+ {
+ return it->second.funcCreate(_params);
+ }
+ catch(synthLib::DeviceException& e)
+ {
+ LOGNET(networkLib::LogLevel::Error, "Failed to create device for plugin '" << _desc.pluginName << "', version " << _desc.pluginVersion << ", id " << _desc.plugin4CC <<
+ ", device creation caused exception: code " << static_cast<uint32_t>(e.errorCode()) << ", message: " << e.what());
+ return nullptr;
+ }
+ }
+
+ bool Import::destroyDevice(const bridgeLib::PluginDesc& _desc, synthLib::Device* _device)
+ {
+ if(!_device)
+ return true;
+
+ std::scoped_lock lock(m_mutex);
+
+ const auto it = m_loadedPlugins.find(_desc);
+ if(it == m_loadedPlugins.end())
+ {
+ assert(false && "plugin unloaded before device destroyed");
+ return false;
+ }
+ it->second.funcDestroy(_device);
+ return true;
+ }
+
+ void Import::findPlugins()
+ {
+ findPlugins(m_config.pluginsPath);
+ findPlugins(synthLib::getModulePath() + "plugins/");
+ }
+
+ void Import::findPlugins(const std::string& _rootPath)
+ {
+ findPlugins(_rootPath, ".dll");
+ findPlugins(_rootPath, ".so");
+ findPlugins(_rootPath, ".dylib");
+
+ findPlugins(_rootPath, ".vst3");
+ findPlugins(_rootPath, ".clap");
+ findPlugins(_rootPath, ".lv2");
+ }
+
+ void Import::findPlugins(const std::string& _rootPath, const std::string& _extension)
+ {
+ const auto path = synthLib::getModulePath() + "plugins/";
+ std::vector<std::string> files;
+ synthLib::findFiles(files, path, _extension, 0, std::numeric_limits<uint32_t>::max());
+
+ for (const auto& file : files)
+ loadPlugin(file);
+ }
+
+ void Import::loadPlugin(const std::string& _file)
+ {
+ // load each plugin lib only once
+ if(m_loadedFiles.find(_file) != m_loadedFiles.end())
+ return;
+
+ Plugin plugin;
+
+ plugin.handle = dlopen(_file.c_str(), RTLD_LAZY);
+ if(!plugin.handle)
+ return;
+
+ plugin.funcCreate = reinterpret_cast<FuncBridgeDeviceCreate>(dlsym(plugin.handle, "bridgeDeviceCreate")); // NOLINT(clang-diagnostic-cast-function-type-strict)
+ plugin.funcDestroy = reinterpret_cast<FuncBridgeDeviceDestroy>(dlsym(plugin.handle, "bridgeDeviceDestroy")); // NOLINT(clang-diagnostic-cast-function-type-strict)
+ plugin.funcGetDesc = reinterpret_cast<FuncBridgeDeviceGetDesc>(dlsym(plugin.handle, "bridgeDeviceGetDesc")); // NOLINT(clang-diagnostic-cast-function-type-strict)
+
+ if(!plugin.funcCreate || !plugin.funcDestroy || !plugin.funcGetDesc)
+ {
+ dlclose(plugin.handle);
+ return;
+ }
+
+ bridgeLib::PluginDesc desc;
+ plugin.funcGetDesc(desc);
+
+ if(desc.plugin4CC.empty() || desc.pluginName.empty() || desc.pluginVersion == 0)
+ {
+ dlclose(plugin.handle);
+ return;
+ }
+
+ if(m_loadedPlugins.find(desc) != m_loadedPlugins.end())
+ {
+ dlclose(plugin.handle);
+ return;
+ }
+
+ LOGNET(networkLib::LogLevel::Info, "Found plugin '" << desc.pluginName << "', version " << desc.pluginVersion << ", id " << desc.plugin4CC);
+
+ m_loadedPlugins.insert({desc, plugin});
+ m_loadedFiles.insert(_file);
+ }
+}
diff --git a/source/bridge/server/import.h b/source/bridge/server/import.h
@@ -0,0 +1,54 @@
+#pragma once
+
+#include <map>
+#include <mutex>
+#include <set>
+
+#include "bridgeLib/commands.h"
+
+namespace synthLib
+{
+ struct DeviceCreateParams;
+ class Device;
+}
+
+namespace bridgeServer
+{
+ struct Config;
+
+ class Import
+ {
+ public:
+ typedef synthLib::Device* (*FuncBridgeDeviceCreate)(const synthLib::DeviceCreateParams& _params);
+ typedef void (*FuncBridgeDeviceDestroy)(synthLib::Device*);
+ typedef void (*FuncBridgeDeviceGetDesc)(bridgeLib::PluginDesc&);
+
+ struct Plugin final
+ {
+ std::string filename;
+ void* handle = nullptr;
+ FuncBridgeDeviceCreate funcCreate = nullptr;
+ FuncBridgeDeviceDestroy funcDestroy = nullptr;
+ FuncBridgeDeviceGetDesc funcGetDesc = nullptr;
+ };
+
+ Import(const Config& _config);
+ ~Import();
+
+ synthLib::Device* createDevice(const synthLib::DeviceCreateParams& _params, const bridgeLib::PluginDesc& _desc);
+ bool destroyDevice(const bridgeLib::PluginDesc& _desc, synthLib::Device* _device);
+
+ private:
+ void findPlugins();
+ void findPlugins(const std::string& _rootPath);
+ void findPlugins(const std::string& _rootPath, const std::string& _extension);
+ void loadPlugin(const std::string& _file);
+
+ const Config& m_config;
+
+ std::map<bridgeLib::PluginDesc, Plugin> m_loadedPlugins;
+ std::set<std::string> m_loadedFiles;
+
+ std::mutex m_mutex;
+ };
+}
diff --git a/source/bridge/server/romPool.cpp b/source/bridge/server/romPool.cpp
@@ -0,0 +1,70 @@
+#include "romPool.h"
+
+#include "config.h"
+#include "baseLib/md5.h"
+#include "networkLib/logging.h"
+#include "synthLib/os.h"
+
+namespace bridgeServer
+{
+ RomPool::RomPool(Config& _config) : m_config(_config)
+ {
+ findRoms();
+ }
+
+ const RomData& RomPool::getRom(const baseLib::MD5& _hash)
+ {
+ std::scoped_lock lock(m_mutex);
+
+ auto it = m_roms.find(_hash);
+ if(it == m_roms.end())
+ findRoms();
+ it = m_roms.find(_hash);
+ if(it != m_roms.end())
+ return it->second;
+ static RomData empty;
+ return empty;
+ }
+
+ void RomPool::addRom(const std::string& _name, const RomData& _data)
+ {
+ std::scoped_lock lock(m_mutex);
+
+ const auto hash = baseLib::MD5(_data);
+ if(m_roms.find(hash) != m_roms.end())
+ return;
+
+ if(synthLib::writeFile(getRootPath() + _name + '_' + hash.toString() + ".bin", _data))
+ m_roms.insert({hash, _data});
+ }
+
+ std::string RomPool::getRootPath() const
+ {
+ return m_config.romsPath;
+ }
+
+ void RomPool::findRoms()
+ {
+ std::vector<std::string> files;
+ synthLib::findFiles(files, getRootPath(), {}, 0, 16 * 1024 * 1024);
+
+ for (const auto& file : files)
+ {
+ std::vector<uint8_t> romData;
+
+ if(!synthLib::readFile(romData, file))
+ {
+ LOGNET(networkLib::LogLevel::Error, "Failed to load file " << file);
+ continue;
+ }
+
+ const auto hash = baseLib::MD5(romData);
+
+ if(m_roms.find(hash) != m_roms.end())
+ continue;
+
+ m_roms.insert({hash, std::move(romData)});
+ LOGNET(networkLib::LogLevel::Info, "Loaded ROM " << synthLib::getFilenameWithoutPath(file));
+ }
+ }
+}
diff --git a/source/bridge/server/romPool.h b/source/bridge/server/romPool.h
@@ -0,0 +1,35 @@
+#pragma once
+
+#include <cstdint>
+#include <map>
+#include <mutex>
+#include <vector>
+
+namespace baseLib
+{
+ class MD5;
+}
+
+namespace bridgeServer
+{
+ struct Config;
+ using RomData = std::vector<uint8_t>;
+
+ class RomPool
+ {
+ public:
+ RomPool(Config& _config);
+
+ const RomData& getRom(const baseLib::MD5& _hash);
+ void addRom(const std::string& _name, const RomData& _data);
+
+ private:
+ std::string getRootPath() const;
+ void findRoms();
+
+ const Config& m_config;
+
+ std::mutex m_mutex;
+ std::map<baseLib::MD5, RomData> m_roms;
+ };
+}
diff --git a/source/bridge/server/server.cpp b/source/bridge/server/server.cpp
@@ -0,0 +1,113 @@
+#include "server.h"
+
+#include <ptypes/pinet.h>
+
+#include "bridgeLib/types.h"
+#include "networkLib/logging.h"
+
+namespace bridgeServer
+{
+ Server::Server(int _argc, char** _argv)
+ : m_config(_argc, _argv)
+ , m_plugins(m_config)
+ , m_romPool(m_config)
+ , m_tcpServer([this](std::unique_ptr<networkLib::TcpStream> _stream){onClientConnected(std::move(_stream));}
+ , bridgeLib::g_tcpServerPort)
+ , m_lastDeviceStateUpdate(std::chrono::system_clock::now())
+ {
+ }
+
+ Server::~Server()
+ {
+ exit(true);
+ m_cvWait.notify_one();
+ m_clients.clear();
+ }
+
+ void Server::run()
+ {
+ while(!m_exit)
+ {
+ std::unique_lock lock(m_cvWaitMutex);
+ m_cvWait.wait_for(lock, std::chrono::seconds(10));
+
+ cleanupClients();
+ doPeriodicDeviceStateUpdate();
+ }
+ }
+
+ void Server::onClientConnected(std::unique_ptr<networkLib::TcpStream> _stream)
+ {
+ const auto s = _stream->getPtypesStream();
+ const std::string name = std::string(ptypes::iptostring(s->get_ip())) + ":" + std::to_string(s->get_port());
+
+ std::scoped_lock lock(m_mutexClients);
+ m_clients.emplace_back(std::make_unique<ClientConnection>(*this, std::move(_stream), name));
+ }
+
+ void Server::onClientException(const ClientConnection&, const networkLib::NetException& _e)
+ {
+ m_cvWait.notify_one();
+ }
+
+ void Server::exit(const bool _exit)
+ {
+ m_exit = _exit;
+ }
+
+ bridgeLib::DeviceState Server::getCachedDeviceState(const bridgeLib::SessionId& _id)
+ {
+ const auto it = m_cachedDeviceStates.find(_id);
+
+ if(it == m_cachedDeviceStates.end())
+ {
+ static bridgeLib::DeviceState s;
+ return s;
+ }
+
+ auto res = std::move(it->second);
+ m_cachedDeviceStates.erase(it);
+ return res;
+ }
+
+ void Server::cleanupClients()
+ {
+ std::scoped_lock lock(m_mutexClients);
+
+ for(auto it = m_clients.begin(); it != m_clients.end();)
+ {
+ const auto& c = *it;
+ if(!c->isValid())
+ {
+ const auto& deviceState = c->getDeviceState();
+
+ if(deviceState.isValid())
+ m_cachedDeviceStates[c->getPluginDesc().sessionId] = deviceState;
+
+ it = m_clients.erase(it);
+ LOGNET(networkLib::LogLevel::Info, "Client removed, now " << m_clients.size() << " clients left");
+ }
+ else
+ {
+ ++it;
+ }
+ }
+ }
+
+ void Server::doPeriodicDeviceStateUpdate()
+ {
+ std::scoped_lock lock(m_mutexClients);
+
+ const auto now = std::chrono::system_clock::now();
+
+ const auto diff = std::chrono::duration_cast<std::chrono::minutes>(now - m_lastDeviceStateUpdate);
+
+ if(diff.count() < static_cast<int>(m_config.deviceStateRefreshMinutes))
+ return;
+
+ m_lastDeviceStateUpdate = now;
+
+ for (const auto& c : m_clients)
+ c->sendDeviceState(synthLib::StateTypeGlobal);
+ }
+}
diff --git a/source/bridge/server/server.h b/source/bridge/server/server.h
@@ -0,0 +1,56 @@
+#pragma once
+
+#include <mutex>
+#include <list>
+#include <condition_variable>
+
+#include "clientConnection.h"
+#include "config.h"
+#include "import.h"
+#include "romPool.h"
+#include "udpServer.h"
+#include "networkLib/tcpServer.h"
+
+namespace bridgeServer
+{
+ class Server
+ {
+ public:
+ Server(int _argc, char** _argv);
+ ~Server();
+
+ void run();
+
+ void onClientConnected(std::unique_ptr<networkLib::TcpStream> _stream);
+ void onClientException(const ClientConnection& _clientConnection, const networkLib::NetException& _e);
+
+ void exit(bool _exit);
+
+ auto& getPlugins() { return m_plugins; }
+ auto& getRomPool() { return m_romPool; }
+
+ bridgeLib::DeviceState getCachedDeviceState(const bridgeLib::SessionId& _id);
+
+ private:
+ void cleanupClients();
+ void doPeriodicDeviceStateUpdate();
+
+ Config m_config;
+
+ Import m_plugins;
+ RomPool m_romPool;
+
+ UdpServer m_udpServer;
+ networkLib::TcpServer m_tcpServer;
+
+ std::mutex m_mutexClients;
+ std::list<std::unique_ptr<ClientConnection>> m_clients;
+ std::map<bridgeLib::SessionId, bridgeLib::DeviceState> m_cachedDeviceStates;
+
+ bool m_exit = false;
+
+ std::mutex m_cvWaitMutex;
+ std::condition_variable m_cvWait;
+ std::chrono::system_clock::time_point m_lastDeviceStateUpdate;
+ };
+}
diff --git a/source/bridge/server/udpServer.cpp b/source/bridge/server/udpServer.cpp
@@ -0,0 +1,76 @@
+#include "udpServer.h"
+
+#include "bridgeLib/commandReader.h"
+#include "bridgeLib/commandWriter.h"
+#include "bridgeLib/error.h"
+#include "bridgeLib/types.h"
+
+namespace bridgeServer
+{
+ UdpServer::UdpServer() : networkLib::UdpServer(bridgeLib::g_udpServerPort)
+ {
+ }
+
+ std::vector<uint8_t> UdpServer::validateRequest(const std::vector<uint8_t>& _request)
+ {
+ // respond with server info if client talks the same protocol version
+ bridgeLib::ErrorCode errorCode = bridgeLib::ErrorCode::UnexpectedCommand;
+ std::string errorMsg;
+
+ {
+ bridgeLib::CommandReader reader([&](const bridgeLib::Command _command, baseLib::BinaryStream& _in)
+ {
+ if(_command != bridgeLib::Command::PluginInfo)
+ return;
+
+ bridgeLib::PluginDesc desc;
+ desc.read(_in);
+ if(desc.protocolVersion != bridgeLib::g_protocolVersion)
+ {
+ errorCode = bridgeLib::ErrorCode::WrongProtocolVersion;
+ errorMsg = "Protocol version doesn't match";
+ }
+ else if(desc.pluginVersion == 0)
+ {
+ errorCode = bridgeLib::ErrorCode::WrongPluginVersion;
+ errorMsg = "Invalid plugin version";
+ }
+ else if(desc.pluginName.empty() && desc.plugin4CC.empty())
+ {
+ errorCode = bridgeLib::ErrorCode::InvalidPluginDesc;
+ errorMsg = "invalid plugin description";
+ }
+ else
+ {
+ errorCode = bridgeLib::ErrorCode::Ok;
+ }
+ });
+ baseLib::BinaryStream bs(_request);
+ reader.read(bs);
+ }
+
+ bridgeLib::CommandWriter w;
+
+ if(errorCode == bridgeLib::ErrorCode::Ok)
+ {
+ bridgeLib::ServerInfo si;
+ si.protocolVersion = bridgeLib::g_protocolVersion;
+ si.portTcp = bridgeLib::g_tcpServerPort;
+ si.portUdp = bridgeLib::g_udpServerPort;
+ si.write(w.build(bridgeLib::Command::ServerInfo));
+ }
+ else
+ {
+ bridgeLib::Error err;
+ err.code = errorCode;
+ err.msg = errorMsg;
+ err.write(w.build(bridgeLib::Command::Error));
+ }
+
+ baseLib::BinaryStream bs;
+ w.write(bs);
+ std::vector<uint8_t> buf;
+ bs.toVector(buf);
+ return buf;
+ }
+}
diff --git a/source/bridge/server/udpServer.h b/source/bridge/server/udpServer.h
@@ -0,0 +1,14 @@
+#pragma once
+
+#include "networkLib/udpServer.h"
+
+namespace bridgeServer
+{
+ class UdpServer : public networkLib::UdpServer
+ {
+ public:
+ UdpServer();
+
+ std::vector<uint8_t> validateRequest(const std::vector<uint8_t>& _request) override;
+ };
+}
diff --git a/source/juce.cmake b/source/juce.cmake
@@ -33,6 +33,9 @@ if(USE_LV2)
list(APPEND juce_formats LV2)
endif()
+add_custom_target(ServerPlugins)
+set_property(TARGET ServerPlugins PROPERTY FOLDER CustomTargets)
+
macro(createJucePlugin targetName productName isSynth plugin4CC binaryDataProject synthLibProject)
juce_add_plugin(${targetName}
# VERSION ... # Set this if the plugin version is different to the project version
@@ -58,7 +61,7 @@ macro(createJucePlugin targetName productName isSynth plugin4CC binaryDataProjec
LV2URI "http://theusualsuspects.lv2.${productName}"
)
- target_sources(${targetName} PRIVATE ${SOURCES})
+ target_sources(${targetName} PRIVATE ${SOURCES} serverPlugin.cpp)
source_group("source" FILES ${SOURCES})
@@ -74,6 +77,12 @@ macro(createJucePlugin targetName productName isSynth plugin4CC binaryDataProjec
JUCE_USE_MP3AUDIOFORMAT=0
JUCE_USE_FLAC=0
JUCE_USE_WINDOWS_MEDIA_FORMAT=0
+
+ PluginName="${productName}"
+ PluginVersionMajor=${CMAKE_PROJECT_VERSION_MAJOR}
+ PluginVersionMinor=${CMAKE_PROJECT_VERSION_MINOR}
+ PluginVersionPatch=${CMAKE_PROJECT_VERSION_PATCH}
+ Plugin4CC="${plugin4CC}"
)
target_link_libraries(${targetName}
@@ -196,6 +205,50 @@ macro(createJucePlugin targetName productName isSynth plugin4CC binaryDataProjec
-P ${JUCE_CMAKE_DIR}/runAuValidation.cmake)
set_tests_properties(${targetName}_AU_Validate PROPERTIES LABELS "PluginTest")
endif()
+
+ # --------- Server Plugin ---------
+
+ set(serverTarget ${productName}ServerPlugin)
+
+ add_library(${serverTarget} SHARED)
+
+ target_compile_definitions(${serverTarget} PUBLIC
+ PluginName="${productName}"
+ PluginVersionMajor=${CMAKE_PROJECT_VERSION_MAJOR}
+ PluginVersionMinor=${CMAKE_PROJECT_VERSION_MINOR}
+ PluginVersionPatch=${CMAKE_PROJECT_VERSION_PATCH}
+ Plugin4CC="${plugin4CC}"
+ )
+ target_sources(${serverTarget} PRIVATE serverPlugin.cpp)
+ target_link_libraries(${serverTarget} ${synthLibProject} bridgeClient)
+ set_property(TARGET ${serverTarget} PROPERTY FOLDER ${targetName})
+
+ # build plugins to the "plugins" dir of the server binary output dir
+ get_target_property(serverOutputDir bridgeServer BINARY_DIR)
+
+ if(NOT serverOutputDir)
+ get_target_property(serverOutputDir bridgeServer RUNTIME_OUTPUT_DIRECTORY)
+ endif()
+
+ if(serverOutputDir)
+ set_property(TARGET ${serverTarget} PROPERTY RUNTIME_OUTPUT_DIRECTORY "${serverOutputDir}/plugins")
+ set_property(TARGET ${serverTarget} PROPERTY LIBRARY_OUTPUT_DIRECTORY "${serverOutputDir}/plugins")
+
+ get_property(isMultiConfig GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
+
+ if(isMultiConfig)
+ set_property(TARGET ${serverTarget} PROPERTY RUNTIME_OUTPUT_DIRECTORY_DEBUG "${serverOutputDir}/Debug/plugins")
+ set_property(TARGET ${serverTarget} PROPERTY RUNTIME_OUTPUT_DIRECTORY_RELEASE "${serverOutputDir}/Release/plugins")
+ set_property(TARGET ${serverTarget} PROPERTY LIBRARY_OUTPUT_DIRECTORY_DEBUG "${serverOutputDir}/Debug/plugins")
+ set_property(TARGET ${serverTarget} PROPERTY LIBRARY_OUTPUT_DIRECTORY_RELEASE "${serverOutputDir}/Release/plugins")
+ endif()
+ endif()
+
+ install(TARGETS ${serverTarget}
+ RUNTIME DESTINATION plugins/ COMPONENT DSPBridgeServer
+ LIBRARY DESTINATION plugins/ COMPONENT DSPBridgeServer)
+
+ add_dependencies(ServerPlugins ${serverTarget})
endmacro()
macro(createJucePluginWithFX targetName productName plugin4CCSynth plugin4CCFX binaryDataProject synthLibProject)
diff --git a/source/jucePluginEditorLib/pluginEditorState.cpp b/source/jucePluginEditorLib/pluginEditorState.cpp
@@ -11,9 +11,18 @@
#include "dsp56kEmu/logging.h"
namespace jucePluginEditorLib
+
+{
+bridgeLib::PluginDesc getPluginDesc(const pluginLib::Processor& _p)
{
+ bridgeLib::PluginDesc pd;
+ _p.getPluginDesc(pd);
+ return pd;
+}
+
PluginEditorState::PluginEditorState(Processor& _processor, pluginLib::Controller& _controller, std::vector<Skin> _includedSkins)
: m_processor(_processor), m_parameterBinding(_controller), m_includedSkins(std::move(_includedSkins))
+ , m_remoteServerList(getPluginDesc(_processor))
{
juce::File(getSkinFolder()).createDirectory();
@@ -302,10 +311,47 @@ void PluginEditorState::openMenu(const juce::MouseEvent* _event)
latencyMenu.addItem("4", true, latency == 4, [this, adjustLatency] { adjustLatency(4); });
latencyMenu.addItem("8", true, latency == 8, [this, adjustLatency] { adjustLatency(8); });
+ auto servers = m_remoteServerList.getEntries();
+
+ juce::PopupMenu deviceTypeMenu;
+ deviceTypeMenu.addItem("Local (default)", true, m_processor.getDeviceType() == pluginLib::DeviceType::Local, [this] { m_processor.setDeviceType(pluginLib::DeviceType::Local); });
+
+ if(servers.empty())
+ {
+ deviceTypeMenu.addItem("- no servers found -", false, false, [this] {});
+ }
+ else
+ {
+ for (const auto & server : servers)
+ {
+ if(server.err.code == bridgeLib::ErrorCode::Ok)
+ {
+ std::string name = server.host + ':' + std::to_string(server.serverInfo.portTcp);
+
+ const auto isSelected = m_processor.getDeviceType() == pluginLib::DeviceType::Remote &&
+ m_processor.getRemoteDeviceHost() == server.host &&
+ m_processor.getRemoteDevicePort() == server.serverInfo.portTcp;
+
+ deviceTypeMenu.addItem(name, true, isSelected, [this, server]
+ {
+ m_processor.setRemoteDevice(server.host, server.serverInfo.portTcp);
+ });
+ }
+ else
+ {
+ std::string name = server.host + " (error " + std::to_string(static_cast<uint32_t>(server.err.code)) + ", " + server.err.msg + ')';
+ deviceTypeMenu.addItem(name, false, false, [this] {});
+ }
+ }
+ }
+
menu.addSubMenu("GUI Skin", skinMenu);
menu.addSubMenu("GUI Scale", scaleMenu);
menu.addSubMenu("Latency (blocks)", latencyMenu);
+ if (m_processor.getConfig().getBoolValue("supportDspBridge", false))
+ menu.addSubMenu("Device Type", deviceTypeMenu);
+
menu.addSeparator();
auto& regions = m_processor.getController().getParameterDescriptions().getRegions();
diff --git a/source/jucePluginEditorLib/pluginEditorState.h b/source/jucePluginEditorLib/pluginEditorState.h
@@ -5,6 +5,8 @@
#include <string>
#include <vector>
+#include "client/serverList.h"
+
#include "skin.h"
#include "jucePluginLib/parameterbinding.h"
@@ -84,5 +86,7 @@ namespace jucePluginEditorLib
float m_rootScale = 1.0f;
std::vector<Skin> m_includedSkins;
std::vector<uint8_t> m_instanceConfig;
+ std::string m_skinFolderName;
+ bridgeClient::ServerList m_remoteServerList;
};
}
diff --git a/source/jucePluginLib/CMakeLists.txt b/source/jucePluginLib/CMakeLists.txt
@@ -55,7 +55,7 @@ target_sources(jucePluginLib PRIVATE ${SOURCES} ${SOURCES_PATCHDB})
source_group("source" FILES ${SOURCES})
source_group("source\\patchdb" FILES ${SOURCES_PATCHDB})
-target_link_libraries(jucePluginLib PUBLIC juceUiLib synthLib)
+target_link_libraries(jucePluginLib PUBLIC juceUiLib synthLib bridgeClient)
target_include_directories(jucePluginLib PUBLIC ../JUCE/modules)
target_compile_definitions(jucePluginLib PRIVATE JUCE_GLOBAL_MODULE_SETTINGS_INCLUDED=1)
set_property(TARGET jucePluginLib PROPERTY FOLDER "Gearmulator")
diff --git a/source/jucePluginLib/dummydevice.cpp b/source/jucePluginLib/dummydevice.cpp
@@ -2,6 +2,10 @@
namespace pluginLib
{
+ DummyDevice::DummyDevice(const synthLib::DeviceCreateParams& _params) : Device(_params)
+ {
+ }
+
void DummyDevice::processAudio(const synthLib::TAudioInputs& _inputs, const synthLib::TAudioOutputs& _outputs, size_t _samples)
{
}
diff --git a/source/jucePluginLib/dummydevice.h b/source/jucePluginLib/dummydevice.h
@@ -7,6 +7,8 @@ namespace pluginLib
class DummyDevice : public synthLib::Device
{
public:
+ explicit DummyDevice(const synthLib::DeviceCreateParams& _params);
+
float getSamplerate() const override { return 44100.0f; }
bool isValid() const override { return false; }
#if !SYNTHLIB_DEMO_MODE
diff --git a/source/jucePluginLib/processor.cpp b/source/jucePluginLib/processor.cpp
@@ -1,16 +1,20 @@
#include "processor.h"
#include "dummydevice.h"
+#include "pluginVersion.h"
#include "tools.h"
#include "types.h"
#include "baseLib/binarystream.h"
+#include "bridgeLib/commands.h"
+
+#include "client/remoteDevice.h"
+
#include "synthLib/deviceException.h"
#include "synthLib/os.h"
#include "synthLib/midiBufferParser.h"
#include "dsp56kEmu/fastmath.h"
-
#include "dsp56kEmu/logging.h"
#include "synthLib/romLoader.h"
@@ -24,7 +28,16 @@ namespace pluginLib
constexpr char g_saveMagic[] = "DSP56300";
constexpr uint32_t g_saveVersion = 2;
- Processor::Processor(const BusesProperties& _busesProperties, Properties _properties) : juce::AudioProcessor(_busesProperties), m_properties(std::move(_properties)), m_midiPorts(*this)
+ bridgeLib::SessionId generateRemoteSessionId()
+ {
+ return std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
+ }
+
+ Processor::Processor(const BusesProperties& _busesProperties, Properties _properties)
+ : juce::AudioProcessor(_busesProperties)
+ , m_properties(std::move(_properties))
+ , m_midiPorts(*this)
+ , m_remoteSessionId(generateRemoteSessionId())
{
juce::File(getPublicRomFolder()).createDirectory();
@@ -147,16 +160,50 @@ namespace pluginLib
if(!m_device)
{
- m_device.reset(new DummyDevice());
+ m_device.reset(new DummyDevice({}));
}
m_device->setDspClockPercent(m_dspClockPercent);
- m_plugin.reset(new synthLib::Plugin(m_device.get()));
+ m_plugin.reset(new synthLib::Plugin(m_device.get(), [this](synthLib::Device* _device)
+ {
+ return onDeviceInvalid(_device);
+ }));
return *m_plugin;
}
+ bridgeClient::RemoteDevice* Processor::createRemoteDevice(const synthLib::DeviceCreateParams& _params)
+ {
+ bridgeLib::PluginDesc desc;
+ getPluginDesc(desc);
+ return new bridgeClient::RemoteDevice(_params, std::move(desc), m_remoteHost, m_remotePort);
+ }
+
+ void Processor::getRemoteDeviceParams(synthLib::DeviceCreateParams& _params) const
+ {
+ _params.preferredSamplerate = getPreferredDeviceSamplerate();
+ _params.hostSamplerate = getHostSamplerate();
+ }
+
+ bridgeClient::RemoteDevice* Processor::createRemoteDevice()
+ {
+ synthLib::DeviceCreateParams params;
+ getRemoteDeviceParams(params);
+ return createRemoteDevice(params);
+ }
+
+ synthLib::Device* Processor::createDevice(const DeviceType _type)
+ {
+ switch (_type)
+ {
+ case DeviceType::Local: return createDevice();
+ case DeviceType::Remote: return createRemoteDevice();
+ case DeviceType::Dummy: return new DummyDevice({});
+ }
+ return nullptr;
+ }
+
bool Processor::setLatencyBlocks(uint32_t _blocks)
{
if (!getPlugin().setLatencyBlocks(_blocks))
@@ -314,14 +361,18 @@ namespace pluginLib
{
if(!m_device)
return {};
- return m_device->getSupportedSamplerates();
+ std::vector<float> result;
+ m_device->getSupportedSamplerates(result);
+ return result;
}
std::vector<float> Processor::getDevicePreferredSamplerates() const
{
if(!m_device)
return {};
- return m_device->getPreferredSamplerates();
+ std::vector<float> result;
+ m_device->getPreferredSamplerates(result);
+ return result;
}
std::optional<std::pair<const char*, uint32_t>> Processor::findResource(const std::string& _filename) const
@@ -374,6 +425,51 @@ namespace pluginLib
return name;
}
+ void Processor::getPluginDesc(bridgeLib::PluginDesc& _desc) const
+ {
+ _desc.plugin4CC = getProperties().plugin4CC;
+ _desc.pluginName = getProperties().name;
+ _desc.pluginVersion = Version::getVersionNumber();
+ _desc.sessionId = m_remoteSessionId;
+ }
+
+ void Processor::setDeviceType(const DeviceType _type, const bool _forceChange/* = false*/)
+ {
+ if(m_deviceType == _type && !_forceChange)
+ return;
+
+ try
+ {
+ if(auto* dev = createDevice(_type))
+ {
+ getPlugin().setDevice(dev);
+ (void)m_device.release();
+ m_device.reset(dev);
+ m_deviceType = _type;
+ }
+ }
+ catch(synthLib::DeviceException& e)
+ {
+ juce::NativeMessageBox::showMessageBox(juce::MessageBoxIconType::WarningIcon,
+ getName() + " - Failed to switch device type",
+ std::string("Failed to create device:\n\n") +
+ e.what() + "\n\n");
+ }
+
+ if(_type != DeviceType::Remote)
+ m_remoteSessionId = generateRemoteSessionId();
+ }
+
+ void Processor::setRemoteDevice(const std::string& _host, const uint32_t _port)
+ {
+ if(m_remotePort == _port && m_remoteHost == _host && m_deviceType == DeviceType::Remote)
+ return;
+
+ m_remoteHost = _host;
+ m_remotePort = _port;
+ setDeviceType(DeviceType::Remote, true);
+ }
+
void Processor::destroyController()
{
m_controller.reset();
@@ -752,6 +848,42 @@ namespace pluginLib
return 0.0f;
}
+ synthLib::Device* Processor::onDeviceInvalid(synthLib::Device* _device)
+ {
+ if(dynamic_cast<bridgeClient::RemoteDevice*>(_device))
+ {
+ try
+ {
+ // attempt one reconnect
+ auto* newDevice = createRemoteDevice();
+ if(newDevice && newDevice->isValid())
+ {
+ m_device.reset(newDevice);
+ return newDevice;
+ }
+ }
+ catch (synthLib::DeviceException& e)
+ {
+ juce::MessageManager::callAsync([e]
+ {
+ juce::NativeMessageBox::showMessageBox(juce::MessageBoxIconType::WarningIcon,
+ "Device creation failed:",
+ std::string("The connection to the remote server has been lost and a reconnect failed. Processing mode has been switched to local processing\n\n") +
+ e.what() + "\n\n");
+ });
+ }
+ }
+
+ setDeviceType(DeviceType::Local);
+
+ juce::MessageManager::callAsync([this]
+ {
+ getController().onStateLoaded();
+ });
+
+ return m_device.get();
+ }
+
bool Processor::rebootDevice()
{
try
diff --git a/source/jucePluginLib/processor.h b/source/jucePluginLib/processor.h
@@ -7,8 +7,20 @@
#include "controller.h"
#include "midiports.h"
+#include "bridgeLib/types.h"
+
#include "synthLib/plugin.h"
+namespace bridgeClient
+{
+ class RemoteDevice;
+}
+
+namespace bridgeLib
+{
+ struct PluginDesc;
+}
+
namespace baseLib
{
class BinaryStream;
@@ -17,6 +29,7 @@ namespace baseLib
namespace synthLib
{
+ struct DeviceCreateParams;
class Plugin;
struct SMidiEvent;
}
@@ -42,6 +55,7 @@ namespace pluginLib
const bool wantsMidiInput;
const bool producesMidiOut;
const bool isMidiEffect;
+ const std::string plugin4CC;
const std::string lv2Uri;
BinaryDataRef binaryData;
};
@@ -59,6 +73,10 @@ namespace pluginLib
synthLib::Plugin& getPlugin();
virtual synthLib::Device* createDevice() = 0;
+ virtual bridgeClient::RemoteDevice* createRemoteDevice(const synthLib::DeviceCreateParams& _params);
+ virtual void getRemoteDeviceParams(synthLib::DeviceCreateParams& _params) const;
+ virtual bridgeClient::RemoteDevice* createRemoteDevice();
+ synthLib::Device* createDevice(DeviceType _type);
bool hasController() const
{
@@ -129,6 +147,15 @@ namespace pluginLib
std::string getConfigFile(bool _useFxFolder = false) const;
std::string getProductName(bool _useFxName = false) const;
+ void getPluginDesc(bridgeLib::PluginDesc& _desc) const;
+
+ void setDeviceType(DeviceType _type, bool _forceChange = false);
+ void setRemoteDevice(const std::string& _host, uint32_t _port);
+ const auto& getRemoteDeviceHost() const { return m_remoteHost; }
+ const auto& getRemoteDevicePort() const { return m_remotePort; }
+
+ auto getDeviceType() const { return m_deviceType; }
+
protected:
void destroyController();
@@ -169,6 +196,8 @@ namespace pluginLib
synthLib::DeviceError getDeviceError() const { return m_deviceError; }
+ synthLib::Device* onDeviceInvalid(synthLib::Device* _device);
+
protected:
synthLib::DeviceError m_deviceError = synthLib::DeviceError::None;
std::unique_ptr<synthLib::Device> m_device;
@@ -184,5 +213,9 @@ namespace pluginLib
float m_hostSamplerate = 0.0f;
MidiPorts m_midiPorts;
BypassBuffer m_bypassBuffer;
+ DeviceType m_deviceType = DeviceType::Local;
+ std::string m_remoteHost;
+ uint32_t m_remotePort = 0;
+ bridgeLib::SessionId m_remoteSessionId;
};
}
diff --git a/source/jucePluginLib/processorPropertiesInit.h b/source/jucePluginLib/processorPropertiesInit.h
@@ -14,6 +14,7 @@ namespace pluginLib
JucePlugin_WantsMidiInput,
JucePlugin_ProducesMidiOutput,
JucePlugin_IsMidiEffect,
+ Plugin4CC,
JucePlugin_Lv2Uri,
{
BinaryData::namedResourceListSize,
diff --git a/source/jucePluginLib/types.h b/source/jucePluginLib/types.h
@@ -14,4 +14,11 @@ namespace pluginLib
};
using ParamValue = int32_t;
+
+ enum class DeviceType
+ {
+ Local,
+ Remote,
+ Dummy
+ };
}
diff --git a/source/mqJucePlugin/PluginProcessor.cpp b/source/mqJucePlugin/PluginProcessor.cpp
@@ -8,6 +8,7 @@
#include "jucePluginLib/processorPropertiesInit.h"
#include "mqLib/device.h"
+#include "mqLib/romloader.h"
namespace
{
@@ -52,7 +53,20 @@ namespace mqJucePlugin
}
synthLib::Device* AudioPluginAudioProcessor::createDevice()
{
- return new mqLib::Device();
+ return new mqLib::Device({});
+ }
+
+ void AudioPluginAudioProcessor::getRemoteDeviceParams(synthLib::DeviceCreateParams& _params) const
+ {
+ Processor::getRemoteDeviceParams(_params);
+
+ const auto rom = mqLib::RomLoader::findROM();
+
+ if(rom.isValid())
+ {
+ _params.romData = rom.getData();
+ _params.romName = rom.getFilename();
+ }
}
pluginLib::Controller* AudioPluginAudioProcessor::createController()
diff --git a/source/mqJucePlugin/PluginProcessor.h b/source/mqJucePlugin/PluginProcessor.h
@@ -13,6 +13,8 @@ namespace mqJucePlugin
jucePluginEditorLib::PluginEditorState* createEditorState() override;
synthLib::Device* createDevice() override;
+ void getRemoteDeviceParams(synthLib::DeviceCreateParams& _params) const override;
+
pluginLib::Controller* createController() override;
private:
diff --git a/source/mqJucePlugin/serverPlugin.cpp b/source/mqJucePlugin/serverPlugin.cpp
@@ -0,0 +1,9 @@
+// ReSharper disable once CppUnusedIncludeDirective
+#include "client/plugin.h"
+
+#include "mqLib/device.h"
+
+synthLib::Device* createBridgeDevice(const synthLib::DeviceCreateParams& _params)
+{
+ return new mqLib::Device(_params);
+}
diff --git a/source/mqLib/device.cpp b/source/mqLib/device.cpp
@@ -7,7 +7,11 @@
namespace mqLib
{
- Device::Device() : m_mq(BootMode::Default), m_state(m_mq), m_sysexRemote(m_mq)
+ Device::Device(const synthLib::DeviceCreateParams& _params)
+ : wLib::Device(_params)
+ , m_mq(BootMode::Default, _params.romData, _params.romName)
+ , m_state(m_mq)
+ , m_sysexRemote(m_mq)
{
// we need to hit the play button to resume boot if the used rom is an OS update. mQ will complain about an uninitialized ROM area in this case
m_mq.setButton(Buttons::ButtonType::Play, true);
diff --git a/source/mqLib/device.h b/source/mqLib/device.h
@@ -16,7 +16,7 @@ namespace mqLib
class Device : public wLib::Device
{
public:
- Device();
+ Device(const synthLib::DeviceCreateParams& _params);
~Device() override;
uint32_t getInternalLatencyMidiToOutput() const override;
uint32_t getInternalLatencyInputToOutput() const override;
diff --git a/source/mqLib/microq.cpp b/source/mqLib/microq.cpp
@@ -13,9 +13,20 @@
namespace mqLib
{
- MicroQ::MicroQ(BootMode _bootMode/* = BootMode::Default*/)
+ ROM initRom(const std::vector<uint8_t>& _romData, const std::string& _romName)
{
- const auto romFile = RomLoader::findROM();
+ if(_romData.empty())
+ return RomLoader::findROM();
+ ROM rom(_romData, _romName);
+ if(rom.isValid())
+ return rom;
+ return RomLoader::findROM();
+ }
+
+ MicroQ::MicroQ(BootMode _bootMode/* = BootMode::Default*/, const std::vector<uint8_t>& _romData, const std::string& _romName)
+ {
+ const ROM romFile = initRom(_romData, _romName);
+
if(!romFile.isValid())
throw synthLib::DeviceException(synthLib::DeviceError::FirmwareMissing, "Failed to find device operating system file mq_2_23.mid.");
diff --git a/source/mqLib/microq.h b/source/mqLib/microq.h
@@ -23,7 +23,7 @@ namespace mqLib
class MicroQ
{
public:
- MicroQ(BootMode _bootMode = BootMode::Default);
+ MicroQ(BootMode _bootMode = BootMode::Default, const std::vector<uint8_t>& _romData = {}, const std::string& _romName = {});
~MicroQ();
// returns true if the instance is valid, false if the initialization failed
diff --git a/source/mqLib/mqmc.cpp b/source/mqLib/mqmc.cpp
@@ -31,7 +31,7 @@ namespace mqLib
if(!_rom.isValid())
return;
m_romRuntimeData.resize(ROM::size());
- memcpy(m_romRuntimeData.data(), m_rom.getData(), ROM::size());
+ memcpy(m_romRuntimeData.data(), m_rom.getData().data(), ROM::size());
m_flash.reset(new hwLib::Am29f(m_romRuntimeData.data(), m_romRuntimeData.size(), false, true));
diff --git a/source/mqLib/rom.cpp b/source/mqLib/rom.cpp
@@ -6,10 +6,20 @@ namespace mqLib
{
ROM::ROM(const std::string& _filename) : wLib::ROM(_filename, g_romSize)
{
- if(getSize() < 5)
+ verifyRom();
+ }
+
+ ROM::ROM(const std::vector<uint8_t>& _data, const std::string& _name) : wLib::ROM(_name, g_romSize, _data)
+ {
+ verifyRom();
+ }
+
+ void ROM::verifyRom()
+ {
+ if(getData().size() < 5)
return;
- auto* d = reinterpret_cast<const char*>(getData());
+ auto* d = reinterpret_cast<const char*>(getData().data());
// OS version is right at the start of the ROM, as zero-terminated ASCII
if(strstr(d, "2.23") != d)
diff --git a/source/mqLib/rom.h b/source/mqLib/rom.h
@@ -11,9 +11,13 @@ namespace mqLib
ROM() = default;
explicit ROM(const std::string& _filename);
+ explicit ROM(const std::vector<uint8_t>& _data, const std::string& _name);
static constexpr uint32_t size() { return g_romSize; }
uint32_t getSize() const override { return g_romSize; }
+
+ private:
+ void verifyRom();
};
}
diff --git a/source/mqTestConsole/mqTestConsole.cpp b/source/mqTestConsole/mqTestConsole.cpp
@@ -76,22 +76,16 @@ int main(int _argc, char* _argv[])
std::string devNameMidiOut;
std::string devNameAudioOut;
- try
- {
- baseLib::ConfigFile cfg((synthLib::getModulePath() + "config.cfg").c_str());
- for (const auto& v : cfg.getValues())
- {
- if(v.first == "MidiIn")
- devNameMidiIn = v.second;
- else if(v.first == "MidiOut")
- devNameMidiOut = v.second;
- else if(v.first == "AudioOut")
- devNameAudioOut = v.second;
- }
- }
- catch(const std::runtime_error&)
+ baseLib::ConfigFile cfg((synthLib::getModulePath() + "config.cfg").c_str());
+
+ for (const auto& v : cfg.getArgsWithValues())
{
- // no config file available
+ if(v.first == "MidiIn")
+ devNameMidiIn = v.second;
+ else if(v.first == "MidiOut")
+ devNameMidiOut = v.second;
+ else if(v.first == "AudioOut")
+ devNameAudioOut = v.second;
}
bool settingsChanged = false;
diff --git a/source/networkLib/CMakeLists.txt b/source/networkLib/CMakeLists.txt
@@ -0,0 +1,38 @@
+cmake_minimum_required(VERSION 3.10)
+
+project(networkLib)
+
+add_library(networkLib STATIC)
+
+set(SOURCES
+ exception.cpp exception.h
+ logging.cpp logging.h
+ networkThread.cpp
+ networkThread.h
+ stream.cpp
+ stream.h
+ tcpClient.cpp
+ tcpClient.h
+ tcpConnection.cpp
+ tcpConnection.h
+ tcpServer.cpp
+ tcpServer.h
+ tcpStream.cpp
+ tcpStream.h
+ udpClient.cpp
+ udpClient.h
+ udpServer.cpp
+ udpServer.h
+)
+
+target_sources(networkLib PRIVATE ${SOURCES})
+
+source_group("source" FILES ${SOURCES})
+
+target_link_libraries(networkLib PUBLIC ptypes)
+if(MSVC)
+ target_link_libraries(networkLib PUBLIC ws2_32)
+endif()
+
+target_include_directories(networkLib PUBLIC ../)
+set_property(TARGET networkLib PROPERTY FOLDER "Networking")
diff --git a/source/networkLib/exception.cpp b/source/networkLib/exception.cpp
@@ -0,0 +1,5 @@
+#include "exception.h"
+
+namespace networkLib
+{
+}
diff --git a/source/networkLib/exception.h b/source/networkLib/exception.h
@@ -0,0 +1,26 @@
+#pragma once
+
+#include <stdexcept>
+#include <string>
+
+namespace networkLib
+{
+ enum ExceptionType
+ {
+ ConnectionClosed,
+ ConnectionLost
+ };
+
+ class NetException : public std::runtime_error
+ {
+ public:
+ NetException(const ExceptionType _type, const std::string& _err) : std::runtime_error(_err), m_type(_type)
+ {
+ }
+
+ auto type() const { return m_type; }
+
+ private:
+ ExceptionType m_type;
+ };
+}
diff --git a/source/networkLib/logging.cpp b/source/networkLib/logging.cpp
@@ -0,0 +1,39 @@
+#include "logging.h"
+
+#include <iostream>
+
+namespace networkLib
+{
+ constexpr const char* g_logLevelNames[] =
+ {
+ "Debug",
+ "INFO",
+ "WARNING",
+ "ERROR"
+ };
+
+ static_assert(std::size(g_logLevelNames) == static_cast<uint32_t>(LogLevel::Count));
+
+ void logStdOut(LogLevel _level, const char* _func, int _line, const std::string& _message)
+ {
+ std::cout << g_logLevelNames[static_cast<uint32_t>(_level)] << ": " << _func << '@' << _line << ": " << _message << '\n';
+ }
+
+ namespace
+ {
+ LogFunc g_logFunc = logStdOut;
+ }
+
+ void setLogFunc(const LogFunc& _func)
+ {
+ if(!_func)
+ g_logFunc = logStdOut;
+ else
+ g_logFunc = _func;
+ }
+
+ void log(LogLevel _level, const char* _func, int _line, const std::string& _message)
+ {
+ g_logFunc(_level, _func, _line, _message);
+ }
+}
diff --git a/source/networkLib/logging.h b/source/networkLib/logging.h
@@ -0,0 +1,37 @@
+#pragma once
+
+#include <functional>
+#include <string>
+#include <sstream>
+#include <iomanip>
+
+namespace networkLib
+{
+ enum class LogLevel
+ {
+ Debug,
+ Info,
+ Warning,
+ Error,
+
+ Count
+ };
+
+ using LogFunc = std::function<void(LogLevel, const char*, int, const std::string&)>;
+
+ void setLogFunc(const LogFunc& _func);
+
+ void log(LogLevel _level, const char* _func, int _line, const std::string& _message);
+}
+
+#define LOGNET(L, S) \
+do \
+{ \
+ std::stringstream __ss___bridgeLib_logging_h; \
+ __ss___bridgeLib_logging_h << S; \
+ \
+ networkLib::log(L, __func__, __LINE__, __ss___bridgeLib_logging_h.str()); \
+} \
+while(false)
+
+#define HEXN(S, n) std::hex << std::setfill('0') << std::setw(n) << (uint32_t)S
diff --git a/source/networkLib/networkThread.cpp b/source/networkLib/networkThread.cpp
@@ -0,0 +1,40 @@
+#include "networkThread.h"
+
+namespace networkLib
+{
+ NetworkThread::~NetworkThread()
+ {
+ stop();
+ }
+
+ void NetworkThread::threadFunc()
+ {
+ while (!m_exit)
+ threadLoopFunc();
+ }
+
+ void NetworkThread::exit(bool _exit)
+ {
+ m_exit = _exit;
+ }
+
+ void NetworkThread::start()
+ {
+ if(m_thread)
+ return;
+ m_exit = false;
+ m_thread.reset(new std::thread([this]()
+ {
+ threadFunc();
+ }));
+ }
+
+ void NetworkThread::stop()
+ {
+ if(!m_thread)
+ return;
+ m_exit = true;
+ m_thread->join();
+ m_thread.reset();
+ }
+}
diff --git a/source/networkLib/networkThread.h b/source/networkLib/networkThread.h
@@ -0,0 +1,27 @@
+#pragma once
+
+#include <memory>
+#include <thread>
+
+namespace networkLib
+{
+ class NetworkThread
+ {
+ protected:
+ NetworkThread() = default;
+ ~NetworkThread();
+
+ virtual void threadLoopFunc() {}
+ virtual void threadFunc();
+
+ bool exit() const { return m_exit; }
+ void exit(bool _exit);
+
+ void start();
+ void stop();
+
+ private:
+ std::unique_ptr<std::thread> m_thread;
+ bool m_exit = false;
+ };
+}
diff --git a/source/networkLib/stream.cpp b/source/networkLib/stream.cpp
diff --git a/source/networkLib/stream.h b/source/networkLib/stream.h
@@ -0,0 +1,15 @@
+#pragma once
+
+namespace networkLib
+{
+ class Stream
+ {
+ public:
+ virtual void close() = 0;
+ virtual bool isValid() const = 0;
+ virtual bool flush() = 0;
+
+ virtual bool read(void* _buf, uint32_t _byteSize) = 0;
+ virtual bool write(const void* _buf, uint32_t _byteSize) = 0;
+ };
+}
diff --git a/source/networkLib/tcpClient.cpp b/source/networkLib/tcpClient.cpp
@@ -0,0 +1,41 @@
+#include "tcpClient.h"
+
+#include <utility>
+
+#include "logging.h"
+#include "../ptypes/pinet.h"
+
+namespace networkLib
+{
+ TcpClient::TcpClient(std::string _host, const uint32_t _port, OnConnectedFunc _onConnected)
+ : TcpConnection(std::move(_onConnected))
+ , m_host(std::move(_host))
+ , m_port(_port)
+ {
+ start();
+ }
+
+ void TcpClient::threadFunc()
+ {
+ auto* stream = new ptypes::ipstream(m_host.c_str(), static_cast<int>(m_port));
+
+ while(!exit())
+ {
+ try
+ {
+ stream->open();
+
+ if (stream->get_active())
+ {
+ onConnected(stream);
+ break;
+ }
+ }
+ catch (ptypes::exception* e)
+ {
+ LOGNET(LogLevel::Warning, "Network Error: " << static_cast<const char*>(e->get_message()));
+ delete e;
+ }
+ }
+ }
+}
diff --git a/source/networkLib/tcpClient.h b/source/networkLib/tcpClient.h
@@ -0,0 +1,19 @@
+#pragma once
+
+#include "tcpConnection.h"
+
+#include <string>
+
+namespace networkLib
+{
+ class TcpClient : TcpConnection
+ {
+ public:
+ TcpClient(std::string _host, uint32_t _port, OnConnectedFunc _onConnected);
+ protected:
+ void threadFunc() override;
+ private:
+ const std::string m_host;
+ const uint32_t m_port;
+ };
+}
diff --git a/source/networkLib/tcpConnection.cpp b/source/networkLib/tcpConnection.cpp
@@ -0,0 +1,32 @@
+#include "tcpConnection.h"
+
+#include <utility> // std::move
+
+#include "logging.h"
+#include "tcpStream.h"
+
+#include "../ptypes/pinet.h"
+
+#ifndef _WIN32
+# include <netinet/tcp.h> // TCP_NODELAY
+#endif
+
+namespace networkLib
+{
+ TcpConnection::TcpConnection(OnConnectedFunc _onConnected) : m_onConnected(std::move(_onConnected))
+ {
+ }
+
+ void TcpConnection::onConnected(ptypes::ipstream* _stream) const
+ {
+ constexpr int opt = 1;
+ const int res = ::setsockopt(_stream->get_handle(), IPPROTO_TCP, TCP_NODELAY, reinterpret_cast<const char*>(&opt), sizeof(opt));
+ if (res < 0)
+ {
+ const int err = errno;
+ LOGNET(LogLevel::Error, static_cast<const char*>(ptypes::iptostring(_stream->get_myip())) << ": Failed to set socket option TCP_NODELAY, err " << err << ": " << strerror(err));
+ }
+
+ m_onConnected(std::make_unique<TcpStream>(_stream));
+ }
+}
diff --git a/source/networkLib/tcpConnection.h b/source/networkLib/tcpConnection.h
@@ -0,0 +1,30 @@
+#pragma once
+
+#include "networkThread.h"
+
+#include <functional>
+#include <memory>
+
+namespace ptypes
+{
+ class ipstream;
+}
+
+namespace networkLib
+{
+ class TcpStream;
+
+ class TcpConnection : protected NetworkThread
+ {
+ public:
+ using OnConnectedFunc = std::function<void(std::unique_ptr<TcpStream>)>;
+
+ protected:
+ explicit TcpConnection(OnConnectedFunc _onConnected);
+ virtual ~TcpConnection() = default;
+ void onConnected(ptypes::ipstream* _stream) const;
+
+ private:
+ OnConnectedFunc m_onConnected;
+ };
+}
diff --git a/source/networkLib/tcpServer.cpp b/source/networkLib/tcpServer.cpp
@@ -0,0 +1,51 @@
+#include "tcpServer.h"
+
+#include "logging.h"
+
+#include "../ptypes/pinet.h"
+
+namespace ptypes
+{
+ class exception;
+}
+
+namespace networkLib
+{
+ TcpServer::TcpServer(OnConnectedFunc _onConnected, const int _tcpPort) : TcpConnection(std::move(_onConnected)), m_port(_tcpPort)
+ {
+ start();
+ }
+
+ void TcpServer::threadFunc()
+ {
+ ptypes::ipstmserver tcpListener;
+
+ LOGNET(LogLevel::Info, "Waiting for incoming connections on TCP port " << m_port);
+
+ tcpListener.bindall(m_port);
+
+ ptypes::ipstream* stream = new ptypes::ipstream();
+
+ while (!exit())
+ {
+ try
+ {
+ if (tcpListener.serve(*stream, -1, 1000))
+ {
+ LOGNET(LogLevel::Info, "Client " << static_cast<const char*>(ptypes::iptostring(stream->get_ip())) << ":" << stream->get_port() << " connected");
+ onConnected(stream);
+ stream = new ptypes::ipstream();
+ }
+ }
+ catch (ptypes::exception* e)
+ {
+ LOGNET(LogLevel::Warning, "Network Error: " << static_cast<const char*>(e->get_message()));
+ delete e;
+ }
+ }
+
+ LOGNET(LogLevel::Info, "TCP server shutdown");
+ delete stream;
+ stream = nullptr;
+ }
+}
diff --git a/source/networkLib/tcpServer.h b/source/networkLib/tcpServer.h
@@ -0,0 +1,17 @@
+#pragma once
+
+#include "tcpConnection.h"
+
+namespace networkLib
+{
+ class TcpServer : TcpConnection
+ {
+ public:
+ TcpServer(OnConnectedFunc _onConnected, int _tcpPort);
+ protected:
+ void threadFunc() override;
+
+ private:
+ const int m_port;
+ };
+}
diff --git a/source/networkLib/tcpStream.cpp b/source/networkLib/tcpStream.cpp
@@ -0,0 +1,81 @@
+#include "tcpStream.h"
+
+#include <cstdint>
+
+#include "exception.h"
+#include "ptypes/pinet.h"
+
+namespace networkLib
+{
+ TcpStream::TcpStream(ptypes::ipstream* _stream) : m_stream(_stream)
+ {
+ }
+
+ TcpStream::~TcpStream()
+ {
+ TcpStream::close();
+ delete m_stream;
+ m_stream = nullptr;
+ }
+
+ void TcpStream::close()
+ {
+ if(!m_stream)
+ return;
+ m_stream->close();
+ }
+
+ bool TcpStream::isValid() const
+ {
+ return m_stream && m_stream->get_active();
+ }
+
+ bool TcpStream::flush()
+ {
+ if (!isValid())
+ return false;
+
+ m_stream->flush();
+ return true;
+ }
+
+ bool TcpStream::read(void* _buf, const uint32_t _byteSize)
+ {
+ if (!isValid())
+ throw NetException(ConnectionClosed, "Couldn't read");
+
+ try
+ {
+ const auto numRead = static_cast<uint32_t>(m_stream->read(_buf, static_cast<int>(_byteSize)));
+ if(numRead == _byteSize)
+ return true;
+ throw NetException(ConnectionClosed, "Couldn't read");
+ }
+ catch(ptypes::exception* e) // NOLINT(misc-throw-by-value-catch-by-reference)
+ {
+ const std::string msg(e->get_message());
+ delete e;
+ throw NetException(ConnectionLost, msg);
+ }
+ }
+
+ bool TcpStream::write(const void* _buf, const uint32_t _byteSize)
+ {
+ if(!isValid())
+ throw NetException(ConnectionClosed, "Couldn't write");
+
+ try
+ {
+ const auto numWritten = static_cast<uint32_t>(m_stream->write(_buf, static_cast<int>(_byteSize)));
+ if(_byteSize == numWritten)
+ return true;
+ throw NetException(ConnectionClosed, "Couldn't write");
+ }
+ catch(ptypes::exception* e) // NOLINT(misc-throw-by-value-catch-by-reference)
+ {
+ const std::string msg(e->get_message());
+ delete e;
+ throw NetException(ConnectionLost, msg);
+ }
+ }
+}
diff --git a/source/networkLib/tcpStream.h b/source/networkLib/tcpStream.h
@@ -0,0 +1,32 @@
+#pragma once
+
+#include <cstdint>
+
+#include "stream.h"
+
+namespace ptypes
+{
+ class ipstream;
+}
+
+namespace networkLib
+{
+ class TcpStream : public Stream
+ {
+ public:
+ explicit TcpStream(ptypes::ipstream* _stream);
+ virtual ~TcpStream();
+
+ void close() override;
+ bool isValid() const override;
+ bool flush() override;
+
+ auto* getPtypesStream() const { return m_stream; }
+
+ private:
+ bool read(void* _buf, uint32_t _byteSize) override;
+ bool write(const void* _buf, uint32_t _byteSize) override;
+
+ ptypes::ipstream* m_stream;
+ };
+}
diff --git a/source/networkLib/udpClient.cpp b/source/networkLib/udpClient.cpp
@@ -0,0 +1,129 @@
+#include "udpClient.h"
+
+#include "logging.h"
+
+#include "../ptypes/pinet.h"
+
+#ifdef _WIN32
+#define NOMINMAX
+#include <Windows.h>
+#include <iphlpapi.h>
+#include <ws2tcpip.h>
+#pragma comment(lib, "Iphlpapi.lib")
+#else
+#include <ifaddrs.h>
+#endif
+
+namespace networkLib
+{
+ UdpClient::UdpClient(const int _udpPort) : m_port(_udpPort)
+ {
+ getBroadcastAddresses(m_broadcastAddresses);
+ if(m_broadcastAddresses.empty())
+ m_broadcastAddresses.push_back(0xff'ff'ff'ff);
+ }
+
+ void UdpClient::getBroadcastAddresses(std::vector<uint32_t>& _addresses)
+ {
+#ifdef _WIN32
+ ULONG bufferSize = 0;
+ if (GetAdaptersInfo(nullptr, &bufferSize) != ERROR_BUFFER_OVERFLOW)
+ return;
+
+ std::vector<BYTE> buffer;
+ buffer.resize(bufferSize, 0);
+
+ if (GetAdaptersInfo(reinterpret_cast<IP_ADAPTER_INFO*>(buffer.data()), &bufferSize) != ERROR_SUCCESS)
+ return;
+
+ const IP_ADAPTER_INFO* adapterInfo = reinterpret_cast<IP_ADAPTER_INFO*>(buffer.data());
+
+ for(; adapterInfo != nullptr; adapterInfo = adapterInfo->Next)
+ {
+ IN_ADDR addrIp;
+ IN_ADDR addrMask;
+
+ inet_pton(AF_INET, adapterInfo->IpAddressList.IpAddress.String, &addrIp);
+ inet_pton(AF_INET, adapterInfo->IpAddressList.IpMask.String, &addrMask);
+
+ const auto broadcastAddr = addrIp.S_un.S_addr | ~addrMask.S_un.S_addr;
+
+ if(!broadcastAddr)
+ continue;
+
+ _addresses.push_back(broadcastAddr);
+ }
+#else
+ ifaddrs* ifap = nullptr;
+ if (getifaddrs(&ifap) == 0 && ifap)
+ {
+ struct ifaddrs * p = ifap;
+ while(p)
+ {
+ auto toUint32 = [](sockaddr* a) -> uint32_t
+ {
+ if(!a || a->sa_family != AF_INET)
+ return 0;
+ return ((struct sockaddr_in *)a)->sin_addr.s_addr;
+ };
+
+ const auto ifaAddr = toUint32(p->ifa_addr);
+ const auto maskAddr = toUint32(p->ifa_netmask);
+
+ if (ifaAddr > 0)
+ {
+ const auto mask = ifaAddr | ~maskAddr;
+ if(mask)
+ _addresses.push_back(mask);
+ }
+ p = p->ifa_next;
+ }
+ freeifaddrs(ifap);
+ }
+#endif
+ }
+
+ void UdpClient::threadLoopFunc()
+ {
+ try
+ {
+ const auto& req = getRequestPacket();
+
+ for (const auto broadcastAddress : m_broadcastAddresses)
+ {
+ ptypes::ipmessage msg(ptypes::ipaddress((ptypes::ulong)broadcastAddress), m_port);
+ msg.send(reinterpret_cast<const char*>(req.data()), static_cast<int>(req.size()));
+ std::vector<uint8_t> buffer;
+ buffer.resize(getMaxUdpResponseSize());
+
+ // wait for answer
+ while (msg.waitfor(250))
+ {
+ ptypes::ipaddress addr;
+
+ const auto count = msg.receive(reinterpret_cast<char*>(buffer.data()), static_cast<int>(buffer.size()), addr);
+
+ if(!count)
+ continue;
+
+ buffer.resize(count);
+
+ const auto host = std::string(ptypes::iptostring(addr));
+
+ if(validateResponse(host, buffer))
+ {
+ exit(true);
+ break;
+ }
+ }
+
+ if(exit())
+ break;
+ }
+ }
+ catch(ptypes::exception* e) // NOLINT(misc-throw-by-value-catch-by-reference)
+ {
+ LOGNET(LogLevel::Warning, "Network error: " << static_cast<const char*>(e->get_message()));
+ }
+ }
+}
diff --git a/source/networkLib/udpClient.h b/source/networkLib/udpClient.h
@@ -0,0 +1,30 @@
+#pragma once
+
+#include "networkThread.h"
+
+#include <string>
+#include <cstdint>
+#include <vector>
+
+namespace networkLib
+{
+ class UdpClient : protected NetworkThread
+ {
+ public:
+ UdpClient(int _udpPort);
+ virtual ~UdpClient() = default;
+
+ static void getBroadcastAddresses(std::vector<uint32_t>& _addresses);
+
+ protected:
+ void threadLoopFunc() override;
+
+ virtual const std::vector<uint8_t>& getRequestPacket() = 0;
+ virtual uint32_t getMaxUdpResponseSize() { return 60000; }
+ virtual bool validateResponse(const std::string& _host, const std::vector<uint8_t>& _message) = 0;
+
+ private:
+ const int m_port;
+ std::vector<uint32_t> m_broadcastAddresses;
+ };
+}
diff --git a/source/networkLib/udpServer.cpp b/source/networkLib/udpServer.cpp
@@ -0,0 +1,54 @@
+#include "udpServer.h"
+
+#include <iosfwd>
+#include <thread>
+#include <vector>
+
+#include "logging.h"
+
+#include "ptypes/ptypes.h"
+#include "ptypes/pinet.h"
+
+namespace ptypes
+{
+ class exception;
+}
+
+namespace networkLib
+{
+ void UdpServer::threadFunc()
+ {
+ ptypes::ipmsgserver udpListener;
+ udpListener.bindall(m_port);
+
+ LOGNET(LogLevel::Info, "UDP server started on port " << m_port);
+
+ while (!exit())
+ {
+ std::vector<uint8_t> buffer;
+ buffer.resize(getMaxRequestSize());
+
+ try
+ {
+ // check if any broadcast is there and answer
+ if (udpListener.poll(-1, 1000))
+ {
+ const auto count = udpListener.receive(reinterpret_cast<char*>(buffer.data()), static_cast<int>(buffer.size()));
+ if(!count)
+ continue;
+ buffer.resize(count);
+ const auto response = validateRequest(buffer);
+ if (!response.empty())
+ udpListener.send(reinterpret_cast<const char*>(response.data()), static_cast<int>(response.size()));
+ }
+ }
+ catch (ptypes::exception* e)
+ {
+ LOGNET(LogLevel::Warning, "Network exception: " << static_cast<const char*>(e->get_message()));
+ delete e;
+ }
+ }
+
+ LOGNET(LogLevel::Info, "UDP server terminated");
+ }
+}
diff --git a/source/networkLib/udpServer.h b/source/networkLib/udpServer.h
@@ -0,0 +1,25 @@
+#pragma once
+
+#include <vector>
+
+#include "networkThread.h"
+
+namespace networkLib
+{
+ class UdpServer : NetworkThread
+ {
+ public:
+ UdpServer(const int _udpPort) : m_port(_udpPort)
+ {
+ start();
+ }
+ protected:
+ void threadFunc() override;
+
+ virtual uint32_t getMaxRequestSize() const { return 60000; }
+ virtual std::vector<uint8_t> validateRequest(const std::vector<uint8_t>& _request) = 0;
+
+ private:
+ const int m_port;
+ };
+}
diff --git a/source/nord/n2x/n2xJucePlugin/n2xPluginProcessor.cpp b/source/nord/n2x/n2xJucePlugin/n2xPluginProcessor.cpp
@@ -8,6 +8,7 @@
#include "jucePluginLib/processorPropertiesInit.h"
#include "n2xLib/n2xdevice.h"
+#include "n2xLib/n2xromloader.h"
#include "synthLib/deviceException.h"
@@ -51,12 +52,25 @@ namespace n2xJucePlugin
synthLib::Device* AudioPluginAudioProcessor::createDevice()
{
- auto* d = new n2x::Device();
+ auto* d = new n2x::Device({});
if(!d->isValid())
throw synthLib::DeviceException(synthLib::DeviceError::FirmwareMissing, "A firmware rom (512k .bin) is required, but was not found.");
return d;
}
+ void AudioPluginAudioProcessor::getRemoteDeviceParams(synthLib::DeviceCreateParams& _params) const
+ {
+ Processor::getRemoteDeviceParams(_params);
+
+ auto rom = n2x::RomLoader::findROM();
+
+ if(rom.isValid())
+ {
+ _params.romData.assign(rom.data().begin(), rom.data().end());
+ _params.romName = rom.getFilename();
+ }
+ }
+
pluginLib::Controller* AudioPluginAudioProcessor::createController()
{
return new n2xJucePlugin::Controller(*this);
diff --git a/source/nord/n2x/n2xJucePlugin/n2xPluginProcessor.h b/source/nord/n2x/n2xJucePlugin/n2xPluginProcessor.h
@@ -12,6 +12,8 @@ namespace n2xJucePlugin
jucePluginEditorLib::PluginEditorState* createEditorState() override;
synthLib::Device* createDevice() override;
+ void getRemoteDeviceParams(synthLib::DeviceCreateParams& _params) const override;
+
pluginLib::Controller* createController() override;
private:
diff --git a/source/nord/n2x/n2xJucePlugin/serverPlugin.cpp b/source/nord/n2x/n2xJucePlugin/serverPlugin.cpp
@@ -0,0 +1,9 @@
+// ReSharper disable once CppUnusedIncludeDirective
+#include "client/plugin.h"
+
+#include "n2xLib/n2xdevice.h"
+
+synthLib::Device* createBridgeDevice(const synthLib::DeviceCreateParams& _params)
+{
+ return new n2x::Device(_params);
+}
diff --git a/source/nord/n2x/n2xLib/n2xdevice.cpp b/source/nord/n2x/n2xLib/n2xdevice.cpp
@@ -5,15 +5,13 @@
namespace n2x
{
- Device::Device() : m_state(&m_hardware, &getMidiTranslator())
+ Device::Device(const synthLib::DeviceCreateParams& _params)
+ : synthLib::Device(_params)
+ , m_hardware(_params.romData, _params.romName)
+ , m_state(&m_hardware, &getMidiTranslator())
{
}
- const std::string& Device::getRomFilename() const
- {
- return m_hardware.getRomFilename();
- }
-
float Device::getSamplerate() const
{
return g_samplerate;
diff --git a/source/nord/n2x/n2xLib/n2xdevice.h b/source/nord/n2x/n2xLib/n2xdevice.h
@@ -12,9 +12,7 @@ namespace n2x
class Device : public synthLib::Device
{
public:
- Device();
-
- const std::string& getRomFilename() const;
+ Device(const synthLib::DeviceCreateParams& _params);
float getSamplerate() const override;
bool isValid() const override;
diff --git a/source/nord/n2x/n2xLib/n2xhardware.cpp b/source/nord/n2x/n2xLib/n2xhardware.cpp
@@ -12,8 +12,18 @@ namespace n2x
static_assert((g_syncEsaiFrameRate & (g_syncEsaiFrameRate - 1)) == 0, "esai frame sync rate must be power of two");
static_assert(g_syncHaltDspEsaiThreshold >= g_syncEsaiFrameRate * 2, "esai DSP halt threshold must be greater than two times the sync rate");
- Hardware::Hardware()
- : m_rom(RomLoader::findROM())
+ Rom initRom(const std::vector<uint8_t>& _romData, const std::string& _romName)
+ {
+ if(_romData.empty())
+ return RomLoader::findROM();
+ Rom rom(_romData, _romName);
+ if(rom.isValid())
+ return rom;
+ return RomLoader::findROM();
+ }
+
+ Hardware::Hardware(const std::vector<uint8_t>& _romData, const std::string& _romName)
+ : m_rom(initRom(_romData, _romName))
, m_uc(*this, m_rom)
, m_dspA(*this, m_uc.getHdi08A(), 0)
, m_dspB(*this, m_uc.getHdi08B(), 1)
diff --git a/source/nord/n2x/n2xLib/n2xhardware.h b/source/nord/n2x/n2xLib/n2xhardware.h
@@ -13,7 +13,7 @@ namespace n2x
{
public:
using AudioOutputs = std::array<std::vector<dsp56k::TWord>, 4>;
- Hardware();
+ Hardware(const std::vector<uint8_t>& _romData = {}, const std::string& _romName = {});
~Hardware();
bool isValid() const;
diff --git a/source/nord/n2x/n2xLib/n2xrom.cpp b/source/nord/n2x/n2xLib/n2xrom.cpp
@@ -6,7 +6,7 @@
namespace n2x
{
- Rom::Rom() : RomData()
+ Rom::Rom()
{
if(!isValidRom(data()))
invalidate();
@@ -18,6 +18,12 @@ namespace n2x
invalidate();
}
+ Rom::Rom(const std::vector<uint8_t>& _data, const std::string& _filename) : RomData(_data, _filename)
+ {
+ if(!isValidRom(data()))
+ invalidate();
+ }
+
bool Rom::isValidRom(const std::vector<uint8_t>& _data)
{
constexpr uint8_t key[] = {'n', 'r', '2', 0, 'n', 'L', '2', 0};
diff --git a/source/nord/n2x/n2xLib/n2xrom.h b/source/nord/n2x/n2xLib/n2xrom.h
@@ -10,6 +10,7 @@ namespace n2x
public:
Rom();
Rom(const std::string& _filename);
+ Rom(const std::vector<uint8_t>& _data, const std::string& _filename);
static bool isValidRom(const std::vector<uint8_t>& _data);
};
diff --git a/source/nord/n2x/n2xLib/n2xromdata.cpp b/source/nord/n2x/n2xLib/n2xromdata.cpp
@@ -21,6 +21,14 @@ namespace n2x
m_filename = _filename;
}
+ template <uint32_t Size> RomData<Size>::RomData(const std::vector<uint8_t>& _data, const std::string& _filename)
+ {
+ if(_data.size() != MySize)
+ return;
+ m_data = _data;
+ m_filename = _filename;
+ }
+
template <uint32_t Size> void RomData<Size>::saveAs(const std::string& _filename) const
{
synthLib::writeFile(_filename, m_data);
diff --git a/source/nord/n2x/n2xLib/n2xromdata.h b/source/nord/n2x/n2xLib/n2xromdata.h
@@ -13,6 +13,7 @@ namespace n2x
static constexpr uint32_t MySize = Size;
RomData();
RomData(const std::string& _filename);
+ RomData(const std::vector<uint8_t>& _data, const std::string& _filename);
bool isValid() const { return !m_filename.empty(); }
const auto& data() const { return m_data; }
diff --git a/source/osTIrusJucePlugin/serverPlugin.cpp b/source/osTIrusJucePlugin/serverPlugin.cpp
@@ -0,0 +1,9 @@
+// ReSharper disable once CppUnusedIncludeDirective
+#include "client/plugin.h"
+
+#include "virusLib/device.h"
+
+synthLib::Device* createBridgeDevice(const synthLib::DeviceCreateParams& _params)
+{
+ return new virusLib::Device(_params);
+}
diff --git a/source/osirusJucePlugin/serverPlugin.cpp b/source/osirusJucePlugin/serverPlugin.cpp
@@ -0,0 +1,9 @@
+// ReSharper disable once CppUnusedIncludeDirective
+#include "client/plugin.h"
+
+#include "virusLib/device.h"
+
+synthLib::Device* createBridgeDevice(const synthLib::DeviceCreateParams& _params)
+{
+ return new virusLib::Device(_params);
+}
diff --git a/source/ptypes/CMakeLists.txt b/source/ptypes/CMakeLists.txt
@@ -0,0 +1,66 @@
+cmake_minimum_required(VERSION 3.10)
+
+project(ptypes)
+
+set(SOURCES
+ pmd5.cxx
+ pvariant.cxx
+ pipbase.cxx
+ piobase.cxx
+ ptime.cxx
+ patomic.cxx
+ pputf.cxx
+ pinstm.cxx
+ pnpipe.cxx
+ pstrmanip.cxx
+ pstring.cxx
+ pipstm.cxx
+ pmsgq.cxx
+ ppodlist.cxx
+ pipmsg.cxx
+ pnpserver.cxx
+ pstrlist.cxx
+ pstrconv.cxx
+ poutstm.cxx
+ pipsvbase.cxx
+ prwlock.cxx
+ pthread.cxx
+ pipmsgsv.cxx
+ pstrtoi.cxx
+ pinfilter.cxx
+ pcset.cxx
+ pobjlist.cxx
+ punit.cxx
+ pstdio.cxx
+ pfdxstm.cxx
+ ptimedsem.cxx
+ poutfile.cxx
+ pipstmsv.cxx
+ pcomponent.cxx
+ pinfile.cxx
+ pstrcase.cxx
+ poutmem.cxx
+ ptrigger.cxx
+ pcsetdbg.cxx
+ pinmem.cxx
+ ptextmap.cxx
+ pmem.cxx
+ poutfilter.cxx
+ pstrutils.cxx
+ pfatal.cxx
+ psemaphore.cxx
+ pintee.cxx
+ pasync.cxx
+ ppipe.cxx
+ pmtxtable.cxx
+ pexcept.cxx
+ punknown.cxx
+ pversion.cxx
+)
+
+add_library(ptypes STATIC)
+
+target_sources(ptypes PRIVATE ${SOURCES})
+
+target_include_directories(ptypes PUBLIC ../)
+set_property(TARGET ptypes PROPERTY FOLDER "Networking")
diff --git a/source/ptypes/pasync.cxx b/source/ptypes/pasync.cxx
@@ -0,0 +1,61 @@
+/*
+ *
+ * C++ Portable Types Library (PTypes)
+ * Version 2.1.1 Released 27-Jun-2007
+ *
+ * Copyright (C) 2001-2007 Hovik Melikyan
+ *
+ * http://www.melikyan.com/ptypes/
+ *
+ */
+
+#ifdef WIN32
+# include <windows.h>
+#else
+# include <stdlib.h>
+# include <unistd.h>
+# include <pthread.h>
+# ifdef __sun__
+# include <poll.h>
+# endif
+#endif
+
+#include "pasync.h"
+
+
+namespace ptypes {
+
+
+void ptdecl psleep(uint milliseconds)
+{
+#if defined(WIN32)
+ Sleep(milliseconds);
+#elif defined(__sun__)
+ poll(0, 0, milliseconds);
+#else
+ usleep(milliseconds * 1000);
+#endif
+}
+
+
+pthread_id_t ptdecl pthrself()
+{
+#ifdef WIN32
+ return (int)GetCurrentThreadId();
+#else
+ return pthread_self();
+#endif
+}
+
+
+bool ptdecl pthrequal(pthread_id_t id)
+{
+#ifdef WIN32
+ return GetCurrentThreadId() == (uint)id;
+#else
+ return pthread_equal(pthread_self(), id);
+#endif
+}
+
+
+}
diff --git a/source/ptypes/pasync.h b/source/ptypes/pasync.h
@@ -0,0 +1,542 @@
+/*
+ *
+ * C++ Portable Types Library (PTypes)
+ * Version 2.1.1 Released 27-Jun-2007
+ *
+ * Copyright (C) 2001-2007 Hovik Melikyan
+ *
+ * http://www.melikyan.com/ptypes/
+ *
+ */
+
+#ifndef __PASYNC_H__
+#define __PASYNC_H__
+
+#ifdef WIN32
+# define _WINSOCKAPI_ // prevent inclusion of winsock.h, since we need winsock2.h
+# include <windows.h>
+#else
+# include <pthread.h>
+# ifndef __bsdi__
+# include <semaphore.h>
+# endif
+#endif
+
+#ifndef __PPORT_H__
+#include "pport.h"
+#endif
+
+#ifndef __PTYPES_H__
+#include "ptypes.h"
+#endif
+
+
+namespace ptypes {
+
+//
+// Summary of implementation:
+//
+// atomic increment/decrement/exchange
+// MSVC/BCC/i386: internal, asm
+// GCC/i386: internal, asm
+// GCC/PowerPC: internal, asm
+// GCC/SPARC: internal, asm
+// Other: internal, mutex hash table
+//
+// mutex
+// Win32: Critical section
+// Other: POSIX mutex
+//
+// trigger
+// Win32: Event
+// Other: internal, POSIX condvar/mutex
+//
+// rwlock:
+// Win32: internal, Event/mutex
+// MacOS: internal, POSIX condvar/mutex
+// Other: POSIX rwlock
+//
+// semaphore:
+// Win32: = timedsem
+// MacOS: = timedsem
+// Other: POSIX semaphore
+//
+// timedsem (with timed waiting):
+// Win32: Semaphore
+// Other: internal, POSIX mutex/condvar
+//
+
+
+#ifdef _MSC_VER
+#pragma pack(push, 4)
+#endif
+
+
+#ifdef WIN32
+ typedef int pthread_id_t;
+ typedef HANDLE pthread_t;
+#else
+ typedef pthread_t pthread_id_t;
+#endif
+
+
+ptpublic void ptdecl psleep(uint milliseconds);
+ptpublic bool ptdecl pthrequal(pthread_id_t id); // note: this is NOT the thread handle, use thread::get_id()
+ptpublic pthread_id_t ptdecl pthrself(); // ... same
+
+
+// -------------------------------------------------------------------- //
+// --- mutex ---------------------------------------------------------- //
+// -------------------------------------------------------------------- //
+
+
+#ifdef WIN32
+
+struct ptpublic mutex: public noncopyable
+{
+protected:
+ CRITICAL_SECTION critsec;
+public:
+ mutex() { InitializeCriticalSection(&critsec); }
+ ~mutex() { DeleteCriticalSection(&critsec); }
+ void enter() { EnterCriticalSection(&critsec); }
+ void leave() { LeaveCriticalSection(&critsec); }
+ void lock() { enter(); }
+ void unlock() { leave(); }
+};
+
+
+#else
+
+
+struct ptpublic mutex: public noncopyable
+{
+protected:
+ pthread_mutex_t mtx;
+public:
+ mutex() { pthread_mutex_init(&mtx, 0); }
+ ~mutex() { pthread_mutex_destroy(&mtx); }
+ void enter() { pthread_mutex_lock(&mtx); }
+ void leave() { pthread_mutex_unlock(&mtx); }
+ void lock() { enter(); }
+ void unlock() { leave(); }
+};
+
+#endif
+
+
+//
+// scopelock
+//
+
+class scopelock: public noncopyable
+{
+protected:
+ mutex* mtx;
+public:
+ scopelock(mutex& imtx): mtx(&imtx) { mtx->lock(); }
+ ~scopelock() { mtx->unlock(); }
+};
+
+
+//
+// mutex table for hashed memory locking (undocumented)
+//
+
+#define _MUTEX_HASH_SIZE 29 // a prime number for hashing
+
+#ifdef WIN32
+# define pmemlock mutex
+# define pmementer(m) (m)->lock()
+# define pmemleave(m) (m)->unlock()
+#else
+# define _MTX_INIT PTHREAD_MUTEX_INITIALIZER
+# define pmemlock pthread_mutex_t
+# define pmementer pthread_mutex_lock
+# define pmemleave pthread_mutex_unlock
+#endif
+
+
+ptpublic extern pmemlock _mtxtable[_MUTEX_HASH_SIZE];
+
+#define pgetmemlock(addr) (_mtxtable + pintptr(addr) % _MUTEX_HASH_SIZE)
+
+
+// -------------------------------------------------------------------- //
+// --- trigger -------------------------------------------------------- //
+// -------------------------------------------------------------------- //
+
+
+#ifdef WIN32
+
+class ptpublic trigger: public noncopyable
+{
+protected:
+ HANDLE handle; // Event object
+public:
+ trigger(bool autoreset, bool state);
+ ~trigger() { CloseHandle(handle); }
+ void wait() { WaitForSingleObject(handle, INFINITE); }
+ void post() { SetEvent(handle); }
+ void signal() { post(); }
+ void reset() { ResetEvent(handle); }
+};
+
+
+#else
+
+
+class ptpublic trigger: public noncopyable
+{
+protected:
+ pthread_mutex_t mtx;
+ pthread_cond_t cond;
+ int state;
+ bool autoreset;
+public:
+ trigger(bool autoreset, bool state);
+ ~trigger();
+ void wait();
+ void post();
+ void signal() { post(); }
+ void reset();
+};
+
+#endif
+
+
+// -------------------------------------------------------------------- //
+// --- rwlock --------------------------------------------------------- //
+// -------------------------------------------------------------------- //
+
+
+#if defined(WIN32) || defined(__DARWIN__) || defined(__bsdi__)
+# define __PTYPES_RWLOCK__
+#elif defined(linux)
+ // on Linux rwlocks are included only with -D_GNU_SOURCE.
+ // programs that don't use rwlocks, do not need to define
+ // _GNU_SOURCE either.
+# if defined(_GNU_SOURCE) || defined(__USE_UNIX98)
+# define __POSIX_RWLOCK__
+# endif
+#else
+# define __POSIX_RWLOCK__
+#endif
+
+
+#ifdef __PTYPES_RWLOCK__
+
+struct ptpublic rwlock: protected mutex
+{
+protected:
+#ifdef WIN32
+ HANDLE reading; // Event object
+ HANDLE finished; // Event object
+ int readcnt;
+ int writecnt;
+#else
+ pthread_mutex_t mtx;
+ pthread_cond_t readcond;
+ pthread_cond_t writecond;
+ int locks;
+ int writers;
+ int readers;
+#endif
+public:
+ rwlock();
+ ~rwlock();
+ void rdlock();
+ void wrlock();
+ void unlock();
+ void lock() { wrlock(); }
+};
+
+
+#elif defined(__POSIX_RWLOCK__)
+
+
+struct ptpublic rwlock: public noncopyable
+{
+protected:
+ pthread_rwlock_t rw;
+public:
+ rwlock();
+ ~rwlock() { pthread_rwlock_destroy(&rw); }
+ void rdlock() { pthread_rwlock_rdlock(&rw); }
+ void wrlock() { pthread_rwlock_wrlock(&rw); }
+ void unlock() { pthread_rwlock_unlock(&rw); }
+ void lock() { wrlock(); }
+};
+
+#endif
+
+
+#if defined(__PTYPES_RWLOCK__) || defined(__POSIX_RWLOCK__)
+
+//
+// scoperead & scopewrite
+//
+
+class scoperead: public noncopyable
+{
+protected:
+ rwlock* rw;
+public:
+ scoperead(rwlock& irw): rw(&irw) { rw->rdlock(); }
+ ~scoperead() { rw->unlock(); }
+};
+
+
+class scopewrite: public noncopyable
+{
+protected:
+ rwlock* rw;
+public:
+ scopewrite(rwlock& irw): rw(&irw) { rw->wrlock(); }
+ ~scopewrite() { rw->unlock(); }
+};
+
+
+#endif
+
+
+// -------------------------------------------------------------------- //
+// --- semaphore ------------------------------------------------------ //
+// -------------------------------------------------------------------- //
+
+
+#if defined(WIN32) || defined(__DARWIN__) || defined(__bsdi__)
+# define __SEM_TO_TIMEDSEM__
+#endif
+
+
+#ifdef __SEM_TO_TIMEDSEM__
+
+// map ordinary semaphore to timed semaphore
+
+class timedsem;
+typedef timedsem semaphore;
+
+
+#else
+
+
+class ptpublic semaphore: public unknown
+{
+protected:
+ sem_t handle;
+public:
+ semaphore(int initvalue);
+ virtual ~semaphore();
+
+ void wait();
+ void post();
+ void signal() { post(); }
+
+ void decrement() { return wait(); }
+ void increment() { post(); }
+};
+
+#endif
+
+
+class ptpublic timedsem: public unknown
+{
+protected:
+#ifdef WIN32
+ HANDLE handle;
+#else
+ int count;
+ pthread_mutex_t mtx;
+ pthread_cond_t cond;
+#endif
+public:
+ timedsem(int initvalue);
+ virtual ~timedsem();
+ bool wait(int msecs = -1);
+ void post();
+ void signal() { post(); }
+
+ bool decrement(int msec = -1) { return wait(msec); }
+ void increment() { post(); }
+};
+
+
+// -------------------------------------------------------------------- //
+// --- thread --------------------------------------------------------- //
+// -------------------------------------------------------------------- //
+
+
+class ptpublic thread: public unknown
+{
+protected:
+#ifdef WIN32
+ unsigned id;
+#endif
+ pthread_t handle;
+ int autofree;
+ int running;
+ int signaled;
+ int finished;
+ int freed;
+ int reserved; // for priorities
+ timedsem relaxsem;
+
+ virtual void execute() = 0;
+ virtual void cleanup();
+
+ bool relax(int msecs) { return relaxsem.wait(msecs); }
+
+ friend void _threadepilog(thread* thr);
+
+#ifdef WIN32
+ friend unsigned __stdcall _threadproc(void* arg);
+#else
+ friend void* _threadproc(void* arg);
+#endif
+
+public:
+ thread(bool iautofree);
+ virtual ~thread();
+
+#ifdef WIN32
+ pthread_id_t get_id() { return int(id); }
+#else
+ pthread_id_t get_id() { return handle; }
+#endif
+
+ bool get_running() { return running != 0; }
+ bool get_finished() { return finished != 0; }
+ bool get_signaled() { return signaled != 0; }
+
+ void start();
+ void signal();
+ void waitfor();
+};
+
+
+
+// -------------------------------------------------------------------- //
+// --- jobqueue & msgqueue -------------------------------------------- //
+// -------------------------------------------------------------------- //
+
+
+const int MSG_USER = 0;
+const int MSG_QUIT = -1;
+
+const int DEF_QUEUE_LIMIT = 5000;
+
+class ptpublic message: public unknown
+{
+protected:
+ message* next; // next in the message chain, used internally
+ semaphore* sync; // used internally by msgqueue::send(), when called from a different thread
+ friend class jobqueue; // my friends, job queue and message queue...
+ friend class msgqueue;
+public:
+ int id;
+ pintptr param;
+ pintptr result;
+ message(int iid, pintptr iparam = 0);
+ virtual ~message();
+};
+
+
+class ptpublic jobqueue: public noncopyable
+{
+private:
+ int limit; // queue limit
+ message* head; // queue head
+ message* tail; // queue tail
+ int qcount; // number of items in the queue
+ timedsem sem; // queue semaphore
+ timedsem ovrsem; // overflow semaphore
+ mutex qlock; // critical sections in enqueue and dequeue
+
+protected:
+ bool enqueue(message* msg, int timeout = -1);
+ bool push(message* msg, int timeout = -1);
+ message* dequeue(bool safe = true, int timeout = -1);
+ void purgequeue();
+
+public:
+ jobqueue(int ilimit = DEF_QUEUE_LIMIT);
+ virtual ~jobqueue();
+
+ int get_count() const { return qcount; }
+ int get_limit() const { return limit; }
+
+ bool post(message* msg, int timeout = -1);
+ bool post(int id, pintptr param = 0, int timeout = -1);
+ bool posturgent(message* msg, int timeout = -1);
+ bool posturgent(int id, pintptr param = 0, int timeout = -1);
+ message* getmessage(int timeout = -1);
+
+#ifdef PTYPES19_COMPAT
+ int msgsavail() const { return get_count(); }
+#endif
+};
+
+
+template <class T> class tjobqueue: protected jobqueue
+{
+public:
+ tjobqueue(int ilimit = DEF_QUEUE_LIMIT);
+
+ int get_count() const { return jobqueue::get_count(); }
+ int get_limit() const { return jobqueue::get_limit(); }
+ bool post(T* msg, int timeout = -1) { return jobqueue::post(msg, timeout); }
+ bool posturgent(T* msg, int timeout = -1) { return jobqueue::posturgent(msg, timeout); }
+ T* getmessage(int timeout = -1) { return (T*)jobqueue::getmessage(timeout); }
+};
+
+
+class ptpublic msgqueue: protected jobqueue
+{
+private:
+ mutex thrlock; // lock for the queue processing
+ pthread_id_t owner; // thread ID of the queue processing thread
+
+ pintptr finishmsg(message* msg);
+ void handlemsg(message* msg);
+ void takeownership();
+
+protected:
+ bool quit;
+
+ void defhandler(message& msg);
+ virtual void msghandler(message& msg) = 0;
+
+public:
+ msgqueue(int ilimit = DEF_QUEUE_LIMIT);
+ virtual ~msgqueue();
+
+ // functions calling from the owner thread:
+ void processone(); // process one message, may hang if no msgs in the queue
+ void processmsgs(); // process all available messages and return
+ void run(); // process messages until MSG_QUIT
+
+ // functions calling from any thread:
+ int get_count() const { return jobqueue::get_count(); }
+ int get_limit() const { return jobqueue::get_limit(); }
+ bool post(message* msg, int timeout = -1) { return jobqueue::post(msg, timeout); }
+ bool post(int id, pintptr param = 0, int timeout = -1) { return jobqueue::post(id, param, timeout); }
+ bool posturgent(message* msg, int timeout = -1) { return jobqueue::posturgent(msg, timeout); }
+ bool posturgent(int id, pintptr param = 0, int timeout = -1) { return jobqueue::posturgent(id, param, timeout); }
+ pintptr send(message* msg);
+ pintptr send(int id, pintptr param = 0);
+
+#ifdef PTYPES19_COMPAT
+ int msgsavail() const { return get_count(); }
+#endif
+};
+
+
+#ifdef _MSC_VER
+#pragma pack(pop)
+#endif
+
+
+}
+
+#endif // __PASYNC_H__
diff --git a/source/ptypes/patomic.cxx b/source/ptypes/patomic.cxx
@@ -0,0 +1,377 @@
+/*
+ *
+ * C++ Portable Types Library (PTypes)
+ * Version 2.1.1 Released 27-Jun-2007
+ *
+ * Copyright (C) 2001-2007 Hovik Melikyan
+ *
+ * http://www.melikyan.com/ptypes/
+ *
+ */
+
+#ifdef WIN32
+# include <windows.h>
+#endif
+
+#include "ptypes.h"
+#include "pasync.h" // for pmemlock*
+
+#include <assert.h>
+
+namespace ptypes {
+
+
+#ifdef PTYPES_ST
+// single-threaded version
+
+
+int __PFASTCALL pexchange(int* target, int value)
+{
+ int r = *target;
+ *target = value;
+ return r;
+}
+
+
+void* __PFASTCALL pexchange(void** target, void* value)
+{
+ void* r = *target;
+ *target = value;
+ return r;
+}
+
+
+int __PFASTCALL pincrement(int* target)
+{
+ return ++(*target);
+}
+
+
+int __PFASTCALL pdecrement(int* target)
+{
+ return --(*target);
+}
+
+
+#else
+// multi-threaded version
+
+#if defined(__GNUC__) && (defined(__i386__) || defined(__I386__))
+# define GCC_i386
+#elif defined(__GNUC__) && defined(__ppc__)
+# define GCC_PPC
+#elif defined(_MSC_VER) && defined(_M_IX86)
+# define MSC_i386
+#elif defined(_MSC_VER) && (defined(_M_IA64) || defined(_WIN64))
+# define MSC_x64
+#elif defined(__BORLANDC__) && defined(_M_IX86)
+# define BCC_i386
+#elif defined(__GNUC__) && defined(__sparc__) && !defined(__arch64__)
+# define GCC_sparc
+#endif
+
+
+#if defined(MSC_i386) || defined(BCC_i386)
+
+//
+// atomic operations for Microsoft C or Borland C on Windows
+//
+
+#if defined(_MSC_VER)
+# pragma warning (disable: 4035)
+#elif defined(__BORLANDC__)
+# pragma warn -rvl
+#endif
+
+// !!! NOTE
+// the following functions implement atomic exchange/inc/dec on
+// windows. they are dangerous in that they rely on the calling
+// conventions of MSVC and BCC. the first one passes the first
+// two arguments in ECX and EDX, and the second one - in EAX and
+// EDX.
+
+int __PFASTCALL pincrement(int* v)
+{
+ return InterlockedIncrement((LONG*)v);
+}
+
+
+int __PFASTCALL pdecrement(int* v)
+{
+ return InterlockedDecrement((LONG*)v);
+}
+
+
+int __PFASTCALL pexchange(int* a, int b)
+{
+ return InterlockedExchange((LONG*)a,b);
+}
+
+
+void* __PFASTCALL pexchange(void** a, void* b)
+{
+#ifdef _WIN64
+ return InterlockedExchange64( a,b);
+#else
+ return (void*)InterlockedExchange((LONG*)a,(LONG)b);
+#endif
+}
+
+
+#elif defined(GCC_i386)
+
+//
+// GNU C compiler on any i386 platform (actually 486+ for xadd)
+//
+
+int pexchange(int* target, int value)
+{
+ __asm__ __volatile ("lock ; xchgl (%1),%0" : "+r" (value) : "r" (target));
+ return value;
+}
+
+
+void* pexchange(void** target, void* value)
+{
+ __asm__ __volatile ("lock ; xchgl (%1),%0" : "+r" (value) : "r" (target));
+ return value;
+}
+
+
+int pincrement(int* target)
+{
+ int temp = 1;
+ __asm__ __volatile ("lock ; xaddl %0,(%1)" : "+r" (temp) : "r" (target));
+ return temp + 1;
+}
+
+
+int pdecrement(int* target)
+{
+ int temp = -1;
+ __asm__ __volatile ("lock ; xaddl %0,(%1)" : "+r" (temp) : "r" (target));
+ return temp - 1;
+}
+
+
+#elif defined(GCC_PPC)
+
+//
+// GNU C compiler on any PPC platform
+//
+
+int pexchange(int* target, int value)
+{
+ int temp;
+ __asm__ __volatile (
+"1: lwarx %0,0,%1\n\
+ stwcx. %2,0,%1\n\
+ bne- 1b\n\
+ isync"
+ : "=&r" (temp)
+ : "r" (target), "r" (value)
+ : "cc", "memory"
+ );
+ return temp;
+}
+
+
+void* pexchange(void** target, void* value)
+{
+ void* temp;
+ __asm__ __volatile (
+"1: lwarx %0,0,%1\n\
+ stwcx. %2,0,%1\n\
+ bne- 1b\n\
+ isync"
+ : "=&r" (temp)
+ : "r" (target), "r" (value)
+ : "cc", "memory"
+ );
+ return temp;
+}
+
+
+int pincrement(int* target)
+{
+ int temp;
+ __asm__ __volatile (
+"1: lwarx %0,0,%1\n\
+ addic %0,%0,1\n\
+ stwcx. %0,0,%1\n\
+ bne- 1b\n\
+ isync"
+ : "=&r" (temp)
+ : "r" (target)
+ : "cc", "memory"
+ );
+ return temp;
+}
+
+
+int pdecrement(int* target)
+{
+ int temp;
+ __asm__ __volatile (
+"1: lwarx %0,0,%1\n\
+ addic %0,%0,-1\n\
+ stwcx. %0,0,%1\n\
+ bne- 1b\n\
+ isync"
+ : "=&r" (temp)
+ : "r" (target)
+ : "cc", "memory"
+ );
+ return temp;
+}
+
+
+#elif defined GCC_sparc
+
+//
+// GNU C compiler on SPARC in 32-bit mode (pointers are 32-bit)
+//
+
+// assembly routines defined in patomic.sparc.s
+// we currently don't use CAS in the library, but let it be there
+extern "C" {
+ int __patomic_add(volatile int* __mem, int __val);
+ int __patomic_swap(volatile int* __mem, int __val);
+ int __patomic_cas(volatile int* __mem, int __expected, int __newval);
+}
+
+#define __patomic_swap_p(mem,val) \
+ (void*)(__patomic_swap((int*)(mem), (int)(val)))
+
+
+int pexchange(int* target, int value)
+{
+ return __patomic_swap(target, value);
+}
+
+
+void* pexchange(void** target, void* value)
+{
+ return __patomic_swap_p(target, value);
+}
+
+
+int pincrement(int* target)
+{
+ return __patomic_add(target, 1);
+}
+
+
+int pdecrement(int* target)
+{
+ return __patomic_add(target, -1);
+}
+
+#elif defined __EMSCRIPTEN__
+#include <emscripten/threading.h>
+int pexchange(int* target, int value)
+{
+ return emscripten_atomic_exchange_u32(target, value);
+}
+
+
+void* pexchange(void** target, void* value)
+{
+ return (void*)emscripten_atomic_exchange_u32(*target, (uint32_t)value);
+}
+
+
+int pincrement(int* target)
+{
+ return emscripten_atomic_add_u32(target, 1);
+}
+
+
+int pdecrement(int* target)
+{
+ return emscripten_atomic_add_u32(target, -1);
+}
+
+#elif defined MSC_x64
+
+int pexchange(int* target, int value)
+{
+ assert( sizeof(target) == sizeof(volatile LONG*) );
+
+ return InterlockedExchange((volatile LONG*)target,value);
+}
+
+
+void* pexchange(void** target, void* value)
+{
+ return InterlockedExchangePointer(target,value);
+}
+
+int pincrement(int* target)
+{
+ assert( sizeof(target) == sizeof(volatile LONG*) );
+ return InterlockedIncrement((volatile LONG*)target);
+}
+
+
+int pdecrement(int* target)
+{
+ assert( sizeof(target) == sizeof(volatile LONG*) );
+ return InterlockedDecrement((volatile LONG*)target);
+}
+
+
+#else
+
+//
+// other platforms: mutex locking
+//
+
+int pexchange(int* target, int value)
+{
+ pmemlock* m = pgetmemlock(target);
+ pmementer(m);
+ int r = *target;
+ *target = value;
+ pmemleave(m);
+ return r;
+}
+
+
+void* pexchange(void** target, void* value)
+{
+ pmemlock* m = pgetmemlock(target);
+ pmementer(m);
+ void* r = *target;
+ *target = value;
+ pmemleave(m);
+ return r;
+}
+
+int pincrement(int* target)
+{
+ assert( NULL != _mtxtable );
+ pmemlock* m = pgetmemlock(target);
+ pmementer(m);
+ int r = ++(*target);
+ pmemleave(m);
+ return r;
+}
+
+
+int pdecrement(int* target)
+{
+ pmemlock* m = pgetmemlock(target);
+ pmementer(m);
+ int r = --(*target);
+ pmemleave(m);
+ return r;
+}
+
+#endif
+
+
+#endif
+
+
+}
diff --git a/source/ptypes/pcomponent.cxx b/source/ptypes/pcomponent.cxx
@@ -0,0 +1,102 @@
+/*
+ *
+ * C++ Portable Types Library (PTypes)
+ * Version 2.1.1 Released 27-Jun-2007
+ *
+ * Copyright (C) 2001-2007 Hovik Melikyan
+ *
+ * http://www.melikyan.com/ptypes/
+ *
+ */
+
+#include "ptypes.h"
+
+
+namespace ptypes {
+
+
+component::component()
+ : unknown(), refcount(0), freelist(nil), typeinfo(nil) {}
+
+
+component::~component()
+{
+ if (freelist != nil)
+ {
+ for (int i = 0; i < freelist->get_count(); i++)
+ (*freelist)[i]->freenotify(this);
+ delete freelist;
+ freelist = nil;
+ }
+}
+
+
+void component::freenotify(component*)
+{
+}
+
+
+void component::addnotification(component* obj)
+{
+ if (freelist == nil)
+ freelist = new tobjlist<component>(false);
+ freelist->add(obj);
+}
+
+
+void component::delnotification(component* obj)
+{
+ int i = -1;
+ if (freelist != nil)
+ {
+ i = freelist->indexof(obj);
+ if (i >= 0) {
+ freelist->del(i);
+ if (freelist->get_count() == 0)
+ {
+ delete freelist;
+ freelist = nil;
+ }
+ }
+ }
+ if (i == -1)
+ fatal(CRIT_FIRST + 1, "delnotification() failed: no such object");
+}
+
+
+int component::classid()
+{
+ return CLASS_UNDEFINED;
+}
+
+
+component* ptdecl addref(component* c)
+{
+ if (c != nil)
+#ifdef PTYPES_ST
+ c->refcount++;
+#else
+ pincrement(&c->refcount);
+#endif
+ return c;
+}
+
+
+bool ptdecl release(component* c)
+{
+ if (c != nil)
+ {
+#ifdef PTYPES_ST
+ if (--c->refcount == 0)
+#else
+ if (pdecrement(&c->refcount) == 0)
+#endif
+ delete c;
+ else
+ return false;
+ }
+ return true;
+}
+
+
+}
diff --git a/source/ptypes/pcset.cxx b/source/ptypes/pcset.cxx
@@ -0,0 +1,139 @@
+/*
+ *
+ * C++ Portable Types Library (PTypes)
+ * Version 2.1.1 Released 27-Jun-2007
+ *
+ * Copyright (C) 2001-2007 Hovik Melikyan
+ *
+ * http://www.melikyan.com/ptypes/
+ *
+ */
+
+#include "ptypes.h"
+
+
+namespace ptypes {
+
+
+typedef int* pint;
+
+
+static uchar lbitmask[8] = {0xff, 0xfe, 0xfc, 0xf8, 0xf0, 0xe0, 0xc0, 0x80};
+static uchar rbitmask[8] = {0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff};
+
+
+void cset::include(char min, char max)
+{
+ if (uchar(min) > uchar(max))
+ return;
+ int lidx = uchar(min) / 8;
+ int ridx = uchar(max) / 8;
+ uchar lbits = lbitmask[uchar(min) % 8];
+ uchar rbits = rbitmask[uchar(max) % 8];
+
+ if (lidx == ridx)
+ {
+ data[lidx] |= lbits & rbits;
+ }
+ else
+ {
+ data[lidx] |= lbits;
+ for (int i = lidx + 1; i < ridx; i++)
+ data[i] = -1;
+ data[ridx] |= rbits;
+ }
+
+}
+
+
+char hex4(char c)
+{
+ if (c >= 'a')
+ return uchar(c - 'a' + 10);
+ else if (c >= 'A')
+ return uchar(c - 'A' + 10);
+ else
+ return char(c - '0');
+}
+
+
+static uchar parsechar(const char*& p)
+{
+ uchar ret = *p;
+ if (ret == _csetesc) {
+ p++;
+ ret = *p;
+ if ((ret >= '0' && ret <= '9') || (ret >= 'a' && ret <= 'f') || (ret >= 'A' && ret <= 'F')) {
+ ret = hex4(ret);
+ p++;
+ if (*p != 0)
+ ret = uchar((ret << 4) | hex4(*p));
+ }
+ }
+ return ret;
+}
+
+
+void cset::assign(const char* p)
+{
+ if (*p == '*' && *(p + 1) == 0)
+ fill();
+ else
+ {
+ clear();
+ for (; *p != 0; p++) {
+ uchar left = parsechar(p);
+ if (*(p + 1) == '-') {
+ p += 2;
+ uchar right = parsechar(p);
+ include(left, right);
+ }
+ else
+ include(left);
+ }
+ }
+}
+
+
+void cset::unite(const cset& s)
+{
+ for(int i = 0; i < _csetwords; i++)
+ *(pint(data) + i) |= *(pint(s.data) + i);
+}
+
+
+void cset::subtract(const cset& s)
+{
+ for(int i = 0; i < _csetwords; i++)
+ *(pint(data) + i) &= ~(*(pint(s.data) + i));
+}
+
+
+void cset::intersect(const cset& s)
+{
+ for(int i = 0; i < _csetwords; i++)
+ *(pint(data) + i) &= *(pint(s.data) + i);
+}
+
+
+void cset::invert()
+{
+ for(int i = 0; i < _csetwords; i++)
+ *(pint(data) + i) = ~(*(pint(data) + i));
+}
+
+
+bool cset::le(const cset& s) const
+{
+ for (int i = 0; i < _csetwords; i++)
+ {
+ int w1 = *(pint(data) + i);
+ int w2 = *(pint(s.data) + i);
+ if ((w2 | w1) != w2)
+ return false;
+ }
+ return true;
+}
+
+
+}
diff --git a/source/ptypes/pcsetdbg.cxx b/source/ptypes/pcsetdbg.cxx
@@ -0,0 +1,78 @@
+/*
+ *
+ * C++ Portable Types Library (PTypes)
+ * Version 2.1.1 Released 27-Jun-2007
+ *
+ * Copyright (C) 2001-2007 Hovik Melikyan
+ *
+ * http://www.melikyan.com/ptypes/
+ *
+ */
+
+#include "ptypes.h"
+
+
+namespace ptypes {
+
+
+static char hexchar(uchar c)
+{
+ if (c < 10)
+ return char(c + '0');
+ else
+ return char(c - 10 + 'a');
+}
+
+
+inline bool isprintable(uchar c)
+{
+ return ((c >= ' ') && (c < 127));
+}
+
+
+static string showmember(uchar c)
+{
+ if ((c == '-') || (c == '~'))
+ return string('~') + string(c);
+ else if (isprintable(c))
+ return c;
+ else
+ {
+ string ret = "~ ";
+ ret[1] = hexchar(uchar(c >> 4));
+ ret[2] = hexchar(uchar(c & 0x0f));
+ return ret;
+ }
+}
+
+
+string ptdecl asstring(const cset& s)
+{
+ string ret;
+ int l = -1, r = -1;
+ for(int i = 0; i <= _csetbits; i++)
+ {
+ if (i < _csetbits && uchar(i) & s)
+ {
+ if (l == -1)
+ l = i;
+ else
+ r = i;
+ }
+ else if (l != -1)
+ {
+ concat(ret, showmember(uchar(l)));
+ if (r != -1) {
+ if (r > l + 1)
+ concat(ret, '-');
+ concat(ret, showmember(uchar(r)));
+ }
+ l = -1;
+ r = -1;
+ }
+ }
+ return ret;
+}
+
+
+}
diff --git a/source/ptypes/pexcept.cxx b/source/ptypes/pexcept.cxx
@@ -0,0 +1,35 @@
+/*
+ *
+ * C++ Portable Types Library (PTypes)
+ * Version 2.1.1 Released 27-Jun-2007
+ *
+ * Copyright (C) 2001-2007 Hovik Melikyan
+ *
+ * http://www.melikyan.com/ptypes/
+ *
+ */
+
+#include "ptypes.h"
+
+
+namespace ptypes {
+
+
+exception::exception(const char* imsg)
+ : message(imsg)
+{
+}
+
+
+exception::exception(const string& imsg)
+ : message(imsg)
+{
+}
+
+
+exception::~exception()
+{
+}
+
+
+}
diff --git a/source/ptypes/pfatal.cxx b/source/ptypes/pfatal.cxx
@@ -0,0 +1,63 @@
+/*
+ *
+ * C++ Portable Types Library (PTypes)
+ * Version 2.1.1 Released 27-Jun-2007
+ *
+ * Copyright (C) 2001-2007 Hovik Melikyan
+ *
+ * http://www.melikyan.com/ptypes/
+ *
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "pport.h"
+
+#if defined(WIN32) && !defined(NO_CRIT_MSGBOX)
+# include <windows.h>
+# define CRIT_MSGBOX
+#endif
+
+
+namespace ptypes {
+
+
+static void ptdecl defhandler(int code, const char* msg)
+{
+#ifdef CRIT_MSGBOX
+ char buf[2048];
+ _snprintf(buf, sizeof(buf) - 1, "Fatal [%05x]: %s", code, msg);
+ MessageBoxA(0, buf, "Internal error", MB_OK | MB_ICONSTOP);
+#else
+ fprintf(stderr, "\nInternal [%04x]: %s\n", code, msg);
+#endif
+}
+
+static _pcrithandler crith = defhandler;
+
+
+_pcrithandler ptdecl getcrithandler()
+{
+ return crith;
+}
+
+
+_pcrithandler ptdecl setcrithandler(_pcrithandler newh)
+{
+ _pcrithandler ret = crith;
+ crith = newh;
+ return ret;
+}
+
+
+void ptdecl fatal(int code, const char* msg)
+{
+ if (crith != nil)
+ (*crith)(code, msg);
+ exit(code);
+}
+
+
+}
diff --git a/source/ptypes/pfdxstm.cxx b/source/ptypes/pfdxstm.cxx
@@ -0,0 +1,162 @@
+/*
+ *
+ * C++ Portable Types Library (PTypes)
+ * Version 2.1.1 Released 27-Jun-2007
+ *
+ * Copyright (C) 2001-2007 Hovik Melikyan
+ *
+ * http://www.melikyan.com/ptypes/
+ *
+ */
+
+#ifdef WIN32
+# include <windows.h>
+#else
+# include <unistd.h>
+#endif
+
+#include "pstreams.h"
+
+
+namespace ptypes {
+
+
+#ifdef _MSC_VER
+// disable "'this' : used in base member initializer list" warning
+# pragma warning (disable: 4355)
+#endif
+
+
+fdxoutstm::fdxoutstm(int ibufsize, fdxstm* iin)
+ : outstm(false, ibufsize), in(iin) {}
+
+
+fdxoutstm::~fdxoutstm() {}
+
+
+void fdxoutstm::chstat(int newstat)
+{
+ outstm::chstat(newstat);
+ if (newstat == IO_WRITING)
+ in->chstat(newstat);
+}
+
+
+int fdxoutstm::uerrno()
+{
+ return in->uerrno();
+}
+
+
+const char* fdxoutstm::uerrmsg(int code)
+{
+ return in->uerrmsg(code);
+}
+
+
+string fdxoutstm::get_streamname()
+{
+ return in->get_streamname();
+}
+
+
+void fdxoutstm::doopen()
+{
+}
+
+
+void fdxoutstm::doclose()
+{
+ if (in->active)
+ in->close();
+}
+
+
+int fdxoutstm::dorawwrite(const char* buf, int count)
+{
+ return in->dorawwrite(buf, count);
+}
+
+
+fdxstm::fdxstm(int ibufsize)
+ : instm(ibufsize), out(ibufsize, this)
+{
+ out.in = this;
+ addref(&out);
+}
+
+
+fdxstm::~fdxstm() {}
+
+
+int fdxstm::classid()
+{
+ return CLASS2_FDX;
+}
+
+
+void fdxstm::flush()
+{
+ if (out.active)
+ out.flush();
+}
+
+
+int fdxstm::dorawwrite(const char* buf, int count)
+{
+ if (handle == invhandle)
+ return -1;
+#ifdef WIN32
+ unsigned long ret;
+ if (!WriteFile(HANDLE(handle), buf, count, &ret, nil))
+ {
+ error(uerrno(), "Couldn't write");
+ ret = uint(-1);
+ }
+#else
+ int ret;
+ if ((ret = ::write(handle, buf, count)) < 0)
+ error(uerrno(), "Couldn't write");
+#endif
+ return ret;
+}
+
+
+void fdxstm::set_bufsize(int newval)
+{
+ instm::set_bufsize(newval);
+ out.set_bufsize(newval);
+}
+
+
+void fdxstm::open()
+{
+ instm::open();
+ out.open();
+}
+
+
+void fdxstm::close()
+{
+ instm::close();
+ out.close();
+}
+
+
+void fdxstm::cancel()
+{
+ instm::cancel();
+ out.cancel();
+}
+
+
+large fdxstm::tellx(bool forin)
+{
+ if (forin)
+ return instm::tellx();
+ else
+ return out.tellx();
+}
+
+
+}
diff --git a/source/ptypes/pinet.h b/source/ptypes/pinet.h
@@ -0,0 +1,400 @@
+/*
+ *
+ * C++ Portable Types Library (PTypes)
+ * Version 2.1.1 Released 27-Jun-2007
+ *
+ * Copyright (C) 2001-2007 Hovik Melikyan
+ *
+ * http://www.melikyan.com/ptypes/
+ *
+ */
+
+#ifndef __PINET_H__
+#define __PINET_H__
+
+#ifndef __PPORT_H__
+#include "pport.h"
+#endif
+
+#ifndef __PTYPES_H__
+#include "ptypes.h"
+#endif
+
+#ifndef __PSTREAMS_H__
+#include "pstreams.h"
+#endif
+
+
+#ifdef WIN32
+# include <winsock2.h>
+#else
+# include <netdb.h> // for socklen_t
+# include <sys/types.h>
+# include <sys/socket.h>
+#endif
+
+
+namespace ptypes {
+
+
+#ifdef _MSC_VER
+#pragma pack(push, 4)
+#endif
+
+
+//
+// BSD-compatible socket error codes for Win32
+//
+
+#if defined(WSAENOTSOCK) && !defined(ENOTSOCK)
+
+#define EWOULDBLOCK WSAEWOULDBLOCK
+#define EINPROGRESS WSAEINPROGRESS
+#define EALREADY WSAEALREADY
+#define ENOTSOCK WSAENOTSOCK
+#define EDESTADDRREQ WSAEDESTADDRREQ
+#define EMSGSIZE WSAEMSGSIZE
+#define EPROTOTYPE WSAEPROTOTYPE
+#define ENOPROTOOPT WSAENOPROTOOPT
+#define EPROTONOSUPPORT WSAEPROTONOSUPPORT
+#define ESOCKTNOSUPPORT WSAESOCKTNOSUPPORT
+#define EOPNOTSUPP WSAEOPNOTSUPP
+#define EPFNOSUPPORT WSAEPFNOSUPPORT
+#define EAFNOSUPPORT WSAEAFNOSUPPORT
+#define EADDRINUSE WSAEADDRINUSE
+#define EADDRNOTAVAIL WSAEADDRNOTAVAIL
+#define ENETDOWN WSAENETDOWN
+#define ENETUNREACH WSAENETUNREACH
+#define ENETRESET WSAENETRESET
+#define ECONNABORTED WSAECONNABORTED
+#define ECONNRESET WSAECONNRESET
+#define ENOBUFS WSAENOBUFS
+#define EISCONN WSAEISCONN
+#define ENOTCONN WSAENOTCONN
+#define ESHUTDOWN WSAESHUTDOWN
+#define ETOOMANYREFS WSAETOOMANYREFS
+#define ETIMEDOUT WSAETIMEDOUT
+#define ECONNREFUSED WSAECONNREFUSED
+#define ELOOP WSAELOOP
+// #define ENAMETOOLONG WSAENAMETOOLONG
+#define EHOSTDOWN WSAEHOSTDOWN
+#define EHOSTUNREACH WSAEHOSTUNREACH
+// #define ENOTEMPTY WSAENOTEMPTY
+#define EPROCLIM WSAEPROCLIM
+#define EUSERS WSAEUSERS
+#define EDQUOT WSAEDQUOT
+#define ESTALE WSAESTALE
+#define EREMOTE WSAEREMOTE
+
+// NOTE: these are not errno constants in UNIX!
+#define HOST_NOT_FOUND WSAHOST_NOT_FOUND
+#define TRY_AGAIN WSATRY_AGAIN
+#define NO_RECOVERY WSANO_RECOVERY
+#define NO_DATA WSANO_DATA
+
+#endif
+
+
+// shutdown() constants
+
+#if defined(SD_RECEIVE) && !defined(SHUT_RD)
+# define SHUT_RD SD_RECEIVE
+# define SHUT_WR SD_SEND
+# define SHUT_RDWR SD_BOTH
+#endif
+
+
+// max backlog value for listen()
+
+#ifndef SOMAXCONN
+# define SOMAXCONN -1
+#endif
+
+typedef char* sockval_t;
+
+#ifndef WIN32
+# define closesocket close
+#endif
+
+
+#if (defined(__DARWIN__) && !defined(_SOCKLEN_T)) || defined(WIN32) || defined(__hpux)
+ typedef int psocklen;
+#else
+ typedef socklen_t psocklen;
+#endif
+
+
+// -------------------------------------------------------------------- //
+// --- IP address class and DNS utilities ---------------------------- //
+// -------------------------------------------------------------------- //
+
+//
+// IP address
+//
+
+struct ptpublic ipaddress
+{
+public:
+ union
+ {
+ uchar data[4];
+ ulong ldata;
+ };
+ ipaddress() {}
+ ipaddress(ulong a) { ldata = a; }
+ ipaddress(const ipaddress& a) { ldata = a.ldata; }
+ ipaddress(int a, int b, int c, int d);
+ ipaddress& operator= (ulong a) { ldata = a; return *this; }
+ ipaddress& operator= (const ipaddress& a) { ldata = a.ldata; return *this; }
+ uchar& operator [] (int i) { return data[i]; }
+ operator ulong() const { return ldata; }
+};
+
+
+ptpublic extern ipaddress ipnone;
+ptpublic extern ipaddress ipany;
+ptpublic extern ipaddress ipbcast;
+
+
+//
+// IP peer info: host name, IP and the port name
+// used internally in ipstream and ipmessage
+//
+
+
+class ptpublic ippeerinfo: public noncopyable
+{
+protected:
+ ipaddress ip; // target IP
+ string host; // target host name; either IP or hostname must be specified
+ int port; // target port number
+
+ void notfound(); // throws a (estream*) exception
+
+ ptpublic friend bool ptdecl psockname(int, ippeerinfo&);
+
+public:
+ ippeerinfo();
+ ippeerinfo(ipaddress iip, const string& ihost, int iport);
+
+ ipaddress get_ip(); // resolves the host name if necessary (only once)
+ string get_host(); // performs reverse-lookup if necessary (only once)
+ int get_port() { return port; }
+ void clear();
+ string asstring(bool showport) const;
+};
+
+
+ptpublic string ptdecl iptostring(ipaddress ip);
+ptpublic ipaddress ptdecl phostbyname(const char* name);
+ptpublic string ptdecl phostbyaddr(ipaddress ip);
+ptpublic string ptdecl phostcname(const char* name);
+
+// internal utilities
+ptpublic int ptdecl usockerrno();
+ptpublic const char* ptdecl usockerrmsg(int code);
+ptpublic bool ptdecl psockwait(int handle, int timeout);
+ptpublic bool ptdecl psockname(int handle, ippeerinfo&);
+
+
+// -------------------------------------------------------------------- //
+// --- TCP socket classes -------------------------------------------- //
+// -------------------------------------------------------------------- //
+
+
+// additional IO status codes
+
+const int IO_RESOLVING = 10;
+const int IO_RESOLVED = 11;
+const int IO_CONNECTING = 20;
+const int IO_CONNECTED = 21;
+
+
+//
+// ipstream
+//
+
+class ptpublic ipstream: public fdxstm, public ippeerinfo
+{
+ friend class ipstmserver;
+
+protected:
+ int svsocket; // server socket descriptor, used internally by ipstmserver
+
+#ifdef WIN32
+ // sockets are not compatible with file handles on Windows
+ virtual int dorawread(char* buf, int count);
+ virtual int dorawwrite(const char* buf, int count);
+#endif
+
+ virtual int uerrno();
+ virtual const char* uerrmsg(int code);
+ virtual void doopen();
+ virtual large doseek(large newpos, ioseekmode mode);
+ virtual void doclose();
+ virtual void sockopt(int socket);
+ void closehandle();
+
+public:
+ ipstream();
+ ipstream(ipaddress ip, int port);
+ ipstream(const char* host, int port);
+ ipstream(const string& host, int port);
+ virtual ~ipstream();
+ virtual int classid();
+
+ virtual string get_streamname();
+
+ bool waitfor(int timeout);
+ ipaddress get_myip();
+ int get_myport();
+ void set_ip(ipaddress);
+ void set_host(const string&);
+ void set_host(const char*);
+ void set_port(int);
+};
+
+
+//
+// common internal interfaces for ipstmserver and ipmsgserver
+//
+
+class ipbindinfo: public unknown, public ippeerinfo
+{
+public:
+ int handle;
+
+ ipbindinfo(ipaddress iip, const string& ihost, int iport);
+ virtual ~ipbindinfo();
+};
+
+
+class ptpublic ipsvbase: public unknown
+{
+protected:
+ int socktype;
+ bool active;
+ tobjlist<ipbindinfo> addrlist; // list of local socket addresses to bind to
+
+ void error(ippeerinfo& peer, int code, const char* defmsg);
+ bool dopoll(int* i, int timeout);
+ void setupfds(void* set, int i);
+ virtual void open();
+ virtual void close();
+ virtual void dobind(ipbindinfo*) = 0;
+ virtual void sockopt(int socket);
+
+public:
+ ipsvbase(int isocktype);
+ virtual ~ipsvbase();
+
+ int bind(ipaddress ip, int port);
+ int bindall(int port);
+
+ int get_addrcount() { return addrlist.get_count(); }
+ const ipbindinfo& get_addr(int i) { return *addrlist[i]; }
+ void clear();
+};
+
+
+//
+// ipstmserver
+//
+
+class ptpublic ipstmserver: public ipsvbase
+{
+protected:
+ virtual void dobind(ipbindinfo*);
+
+public:
+ ipstmserver();
+ virtual ~ipstmserver();
+
+ bool poll(int i = -1, int timeout = 0);
+ bool serve(ipstream& client, int i = -1, int timeout = -1);
+};
+
+
+// -------------------------------------------------------------------- //
+// --- UDP socket classes -------------------------------------------- //
+// -------------------------------------------------------------------- //
+
+
+//
+// ipmessage
+//
+
+class ptpublic ipmessage: public unknown, public ippeerinfo
+{
+protected:
+ int handle;
+
+ void error(int code, const char* msg);
+ void open();
+ void close();
+ virtual void sockopt(int socket);
+
+public:
+ ipmessage();
+ ipmessage(ipaddress ip, int port);
+ ipmessage(const char* host, int port);
+ ipmessage(const string& host, int port);
+ virtual ~ipmessage();
+
+ void set_ip(ipaddress iip);
+ void set_host(const string&);
+ void set_host(const char*);
+ void set_port(int);
+ ipaddress get_myip();
+ int get_myport();
+ int get_handle() { return handle; }
+
+ bool waitfor(int timeout);
+ int receive(char* buf, int count, ipaddress& src);
+ int receive(char* buf, int count);
+ string receive(int max, ipaddress& src);
+ string receive(int max);
+ void send(const char* buf, int count);
+ void send(const string& s) { send(s, length(s)); }
+};
+
+
+//
+// ipmsgserver
+//
+
+class ptpublic ipmsgserver: public ipsvbase, public ippeerinfo
+{
+protected:
+ int handle;
+
+ virtual void close();
+ virtual void dobind(ipbindinfo*);
+
+public:
+ ipmsgserver();
+ virtual ~ipmsgserver();
+
+ int get_handle() { return handle; }
+
+ bool poll(int i = -1, int timeout = 0);
+ int receive(char* buf, int count);
+ string receive(int max);
+ void send(const char* buf, int count);
+ void send(const string& s) { send(s, length(s)); }
+ void sendto(const char* buf, int count, ipaddress ip, int port);
+ void sendto(const string& s, ipaddress ip, int port)
+ { sendto(s, length(s), ip, port); }
+};
+
+
+#ifdef _MSC_VER
+#pragma pack(pop)
+#endif
+
+
+}
+
+
+#endif // __PINET_H__
+
diff --git a/source/ptypes/pinfile.cxx b/source/ptypes/pinfile.cxx
@@ -0,0 +1,93 @@
+/*
+ *
+ * C++ Portable Types Library (PTypes)
+ * Version 2.1.1 Released 27-Jun-2007
+ *
+ * Copyright (C) 2001-2007 Hovik Melikyan
+ *
+ * http://www.melikyan.com/ptypes/
+ *
+ */
+
+#ifdef WIN32
+# include <windows.h>
+#else
+# include <fcntl.h>
+# include <unistd.h>
+#endif
+
+#include "pstreams.h"
+
+
+namespace ptypes {
+
+
+// *BSD hack
+#ifndef O_LARGEFILE
+# define O_LARGEFILE 0
+#endif
+
+
+infile::infile()
+ : instm(), filename(), syshandle(invhandle), peerhandle(invhandle) {}
+
+
+infile::infile(const char* ifn)
+ : instm(), filename(ifn), syshandle(invhandle), peerhandle(invhandle) {}
+
+
+infile::infile(const string& ifn)
+ : instm(), filename(ifn), syshandle(invhandle), peerhandle(invhandle) {}
+
+
+infile::~infile()
+{
+ close();
+}
+
+
+int infile::classid()
+{
+ return CLASS2_INFILE;
+}
+
+
+string infile::get_streamname()
+{
+ return filename;
+}
+
+
+void infile::doopen()
+{
+ if (syshandle != invhandle)
+ handle = syshandle;
+ else
+ {
+#ifdef WIN32
+ SECURITY_ATTRIBUTES sa;
+ sa.nLength = sizeof(SECURITY_ATTRIBUTES);
+ sa.lpSecurityDescriptor = NULL;
+ sa.bInheritHandle = TRUE;
+
+ handle = int(CreateFileA(filename, GENERIC_READ,
+ FILE_SHARE_READ | FILE_SHARE_WRITE, &sa, OPEN_EXISTING,
+ FILE_FLAG_SEQUENTIAL_SCAN, 0));
+#else
+ handle = ::open(filename, O_RDONLY | O_LARGEFILE);
+#endif
+ if (handle == invhandle)
+ error(uerrno(), "Couldn't open");
+ }
+}
+
+
+void infile::doclose()
+{
+ instm::doclose();
+ syshandle = invhandle;
+ peerhandle = invhandle;
+}
+
+
+}
diff --git a/source/ptypes/pinfilter.cxx b/source/ptypes/pinfilter.cxx
@@ -0,0 +1,170 @@
+/*
+ *
+ * C++ Portable Types Library (PTypes)
+ * Version 2.1.1 Released 27-Jun-2007
+ *
+ * Copyright (C) 2001-2007 Hovik Melikyan
+ *
+ * http://www.melikyan.com/ptypes/
+ *
+ */
+
+#include <string.h>
+
+#include "pstreams.h"
+
+
+namespace ptypes {
+
+
+string infilter::get_errstmname()
+{
+ if (stm == nil)
+ return get_streamname();
+ else
+ return get_streamname() + ": " + stm->get_errstmname();
+}
+
+
+void infilter::copytobuf(string& s)
+{
+ int n = imin(savecount, length(s));
+ if (n > 0)
+ {
+ memcpy(savebuf, pconst(s), n);
+ savebuf += n;
+ savecount -= n;
+ if (n == savecount)
+ clear(s);
+ else
+ del(s, 0, n);
+ }
+}
+
+
+void infilter::copytobuf(pconst& buf, int& count)
+{
+ int n = imin(savecount, count);
+ if (n > 0)
+ {
+ memcpy(savebuf, buf, n);
+ savebuf += n;
+ savecount -= n;
+ buf += n;
+ count -= n;
+ }
+}
+
+
+bool infilter::copytobuf(char c)
+{
+ if (savecount > 0) {
+ *savebuf = c;
+ savebuf++;
+ savecount--;
+ return true;
+ }
+ else
+ return false;
+}
+
+
+infilter::infilter(instm* istm, int ibufsize)
+ : instm(ibufsize), stm(istm), savebuf(nil), savecount(0)
+{
+ if (stm != nil)
+ stm->addnotification(this);
+}
+
+
+infilter::~infilter()
+{
+ if (stm != nil)
+ stm->delnotification(this);
+}
+
+
+void infilter::freenotify(component* sender)
+{
+ if (sender == stm)
+ {
+ stm = nil;
+ close();
+ }
+}
+
+
+void infilter::doopen()
+{
+ if (stm != nil && !stm->get_active())
+ stm->open();
+}
+
+
+void infilter::doclose()
+{
+ savebuf = nil;
+ savecount = 0;
+ clear(postponed);
+}
+
+
+void infilter::set_stm(instm* istm)
+{
+ close();
+ if (stm != nil)
+ stm->delnotification(this);
+ stm = istm;
+ if (stm != nil)
+ stm->addnotification(this);
+}
+
+
+int infilter::dorawread(char* buf, int count)
+{
+ savebuf = buf;
+ savecount = count;
+ if (!isempty(postponed))
+ copytobuf(postponed);
+ if (savecount > 0 && stm != nil)
+ dofilter();
+ return count - savecount;
+}
+
+
+void infilter::post(const char* buf, int count)
+{
+ if (count > 0)
+ {
+ copytobuf(buf, count);
+ if (count > 0)
+ concat(postponed, buf, count);
+ }
+}
+
+
+void infilter::post(string s)
+{
+ if (!isempty(s))
+ {
+ copytobuf(s);
+ if (!isempty(s))
+ concat(postponed, s);
+ }
+}
+
+
+void infilter::post(const char* s)
+{
+ post(s, strlen(s));
+}
+
+
+void infilter::post(char c)
+{
+ if (!copytobuf(c))
+ concat(postponed, c);
+}
+
+
+}
diff --git a/source/ptypes/pinmem.cxx b/source/ptypes/pinmem.cxx
@@ -0,0 +1,104 @@
+/*
+ *
+ * C++ Portable Types Library (PTypes)
+ * Version 2.1.1 Released 27-Jun-2007
+ *
+ * Copyright (C) 2001-2007 Hovik Melikyan
+ *
+ * http://www.melikyan.com/ptypes/
+ *
+ */
+
+#include "pstreams.h"
+
+
+namespace ptypes {
+
+
+inmemory::inmemory(const string& imem)
+ : instm(length(imem)), mem(imem)
+{
+}
+
+
+inmemory::~inmemory()
+{
+ close();
+}
+
+
+int inmemory::classid()
+{
+ return CLASS2_INMEMORY;
+}
+
+
+void inmemory::bufalloc()
+{
+ bufdata = pchar(pconst(mem));
+ abspos = bufsize = bufend = length(mem);
+}
+
+
+void inmemory::buffree()
+{
+ bufclear();
+ bufdata = nil;
+}
+
+
+void inmemory::bufvalidate()
+{
+ eof = bufpos >= bufend;
+}
+
+
+void inmemory::doopen()
+{
+}
+
+
+void inmemory::doclose()
+{
+}
+
+
+large inmemory::doseek(large, ioseekmode)
+{
+ // normally shouldn't reach this point, because seek is
+ // supposed to happen within the I/O buffer
+ return -1;
+}
+
+
+int inmemory::dorawread(char*, int)
+{
+ return 0;
+}
+
+
+string inmemory::get_streamname()
+{
+ return "mem";
+}
+
+
+large inmemory::seekx(large newpos, ioseekmode mode)
+{
+ if (mode == IO_END)
+ {
+ newpos += bufsize;
+ mode = IO_BEGIN;
+ }
+ return instm::seekx(newpos, mode);
+}
+
+
+void inmemory::set_strdata(const string& data)
+{
+ close();
+ mem = data;
+}
+
+
+}
diff --git a/source/ptypes/pinstm.cxx b/source/ptypes/pinstm.cxx
@@ -0,0 +1,350 @@
+/*
+ *
+ * C++ Portable Types Library (PTypes)
+ * Version 2.1.1 Released 27-Jun-2007
+ *
+ * Copyright (C) 2001-2007 Hovik Melikyan
+ *
+ * http://www.melikyan.com/ptypes/
+ *
+ */
+
+#include <errno.h>
+#include <string.h>
+#include <limits.h>
+
+#ifdef WIN32
+# include <windows.h>
+#else
+# include <unistd.h>
+#endif
+
+#include "pstreams.h"
+
+
+namespace ptypes {
+
+
+instm::instm(int ibufsize): iobase(ibufsize)
+{
+}
+
+
+instm::~instm()
+{
+}
+
+
+int instm::classid()
+{
+ return CLASS_INSTM;
+}
+
+
+int instm::dorawread(char* buf, int count)
+{
+ if (handle == invhandle)
+ return -1;
+#ifdef WIN32
+ unsigned long ret;
+ if (!ReadFile(HANDLE(handle), buf, count, &ret, nil))
+#else
+ int ret;
+ if ((ret = ::read(handle, buf, count)) < 0)
+#endif
+ {
+ int e = uerrno();
+ if (e == EPIPE)
+ ret = 0;
+ else
+ error(e, "Couldn't read");
+ }
+ return ret;
+}
+
+
+int instm::rawread(char* buf, int count)
+{
+ requireactive();
+ try
+ {
+ int ret = dorawread(buf, count);
+ if (ret <= 0) {
+ ret = 0;
+ eof = true;
+ chstat(IO_EOF);
+ }
+ else
+ {
+ abspos += ret;
+ chstat(IO_READING);
+ }
+ return ret;
+ }
+ catch (estream*)
+ {
+ eof = true;
+ chstat(IO_EOF);
+ throw;
+ }
+}
+
+
+large instm::tellx()
+{
+ return abspos - bufend + bufpos;
+}
+
+
+void instm::bufvalidate()
+{
+ requirebuf();
+ bufclear();
+ bufend = rawread(bufdata, bufsize);
+}
+
+
+large instm::seekx(large newpos, ioseekmode mode)
+{
+ if (bufdata != 0 && mode != IO_END)
+ {
+ if (mode == IO_CURRENT)
+ {
+ newpos += tellx();
+ mode = IO_BEGIN;
+ }
+
+ // see if it is possible to seek within the buffer
+ large newbufpos = newpos - (abspos - bufend);
+ if (newbufpos >= 0 && newbufpos <= bufend)
+ {
+ bufpos = (int)newbufpos;
+ eof = false;
+ return tellx();
+ }
+ }
+
+ // if IO_END or if not possible to seek within the buffer
+ return iobase::seekx(newpos, mode);
+}
+
+
+bool instm::get_eof()
+{
+ if (!eof && bufdata != 0 && bufpos >= bufend)
+ bufvalidate();
+ return eof;
+}
+
+
+int instm::get_dataavail()
+{
+ get_eof();
+ return bufend - bufpos;
+}
+
+
+char instm::preview()
+{
+ if (!eof && bufpos >= bufend)
+ bufvalidate();
+ if (eof)
+ return eofchar;
+ return bufdata[bufpos];
+}
+
+
+void instm::putback()
+{
+ requireactive();
+ if (bufpos == 0)
+ fatal(CRIT_FIRST + 14, "putback() failed");
+ bufpos--;
+ eof = false;
+}
+
+
+bool instm::get_eol()
+{
+ char c = preview();
+ return (eof || c == 10 || c == 13);
+}
+
+
+void instm::skipeol()
+{
+ switch (preview())
+ {
+ case 10:
+ get();
+ break;
+ case 13:
+ get();
+ if (preview() == 10)
+ get();
+ break;
+ }
+}
+
+
+char instm::get()
+{
+ char ret = preview();
+ if (!eof)
+ bufpos++;
+ return ret;
+}
+
+
+string instm::token(const cset& chars, int limit)
+{
+ requirebuf();
+ string ret;
+ while (!get_eof())
+ {
+ char* b = bufdata + bufpos;
+ char* e = bufdata + bufend;
+ char* p = b;
+ while (p < e && (*p & chars))
+ p++;
+ int n = p - b;
+ limit -= n;
+ if (limit < 0)
+ {
+ bufpos += n + limit;
+ error(ERANGE, "Token too long");
+ }
+ concat(ret, b, n);
+ bufpos += n;
+ if (p < e)
+ break;
+ }
+ return ret;
+}
+
+
+string instm::token(const cset& chars)
+{
+ return token(chars, INT_MAX);
+}
+
+
+static cset linechars = cset("*") - cset("~0a~0d");
+
+
+string instm::line(int limit)
+{
+ string ret = token(linechars, limit);
+ skipeol();
+ return ret;
+}
+
+
+string instm::line()
+{
+ string ret = token(linechars, INT_MAX);
+ skipeol();
+ return ret;
+}
+
+
+int instm::token(const cset& chars, char* buf, int count)
+{
+ requirebuf();
+ int ret = 0;
+ while (count > 0 && !get_eof())
+ {
+ char* b = bufdata + bufpos;
+ char* e = b + imin(count, bufend - bufpos);
+ char* p = b;
+ while (p < e && (*p & chars))
+ p++;
+ int n = p - b;
+ memcpy(buf, b, n);
+ buf += n;
+ ret += n;
+ count -= n;
+ bufpos += n;
+ if (p < e)
+ break;
+ }
+ return ret;
+}
+
+
+int instm::line(char* buf, int size, bool eateol)
+{
+ int ret = token(linechars, buf, size);
+ if (eateol)
+ skipeol();
+ return ret;
+}
+
+
+int instm::read(void* buf, int count)
+{
+ int ret = 0;
+ if (bufdata == 0)
+ ret = rawread(pchar(buf), count);
+ else
+ {
+ while (count > 0 && !get_eof())
+ {
+ int n = imin(count, bufend - bufpos);
+ memcpy(buf, bufdata + bufpos, n);
+ buf = pchar(buf) + n;
+ ret += n;
+ count -= n;
+ bufpos += n;
+ }
+ }
+ return ret;
+}
+
+
+int instm::skip(int count)
+{
+ int ret = 0;
+ requirebuf();
+ while (count > 0 && !get_eof())
+ {
+ int n = imin(count, bufend - bufpos);
+ ret += n;
+ count -= n;
+ bufpos += n;
+ }
+ return ret;
+}
+
+
+int instm::skiptoken(const cset& chars)
+{
+ int ret = 0;
+ requirebuf();
+ while (!get_eof())
+ {
+ char* b = bufdata + bufpos;
+ char* e = bufdata + bufend;
+ char* p = b;
+ while (p < e && (*p & chars))
+ p++;
+ int n = p - b;
+ bufpos += n;
+ ret += n;
+ if (p < e)
+ break;
+ }
+ return ret;
+}
+
+
+void instm::skipline(bool eateol)
+{
+ if (!get_eol())
+ skiptoken(linechars);
+ if (eateol)
+ skipeol();
+}
+
+
+}
diff --git a/source/ptypes/pintee.cxx b/source/ptypes/pintee.cxx
@@ -0,0 +1,68 @@
+/*
+ *
+ * C++ Portable Types Library (PTypes)
+ * Version 2.1.1 Released 27-Jun-2007
+ *
+ * Copyright (C) 2001-2007 Hovik Melikyan
+ *
+ * http://www.melikyan.com/ptypes/
+ *
+ */
+
+#include "pstreams.h"
+
+
+namespace ptypes {
+
+
+intee::intee(instm* istm, const char* ifn, bool iappend)
+ : infilter(istm, -1), file(ifn, iappend)
+{
+}
+
+
+intee::intee(instm* istm, const string& ifn, bool iappend)
+ : infilter(istm, -1), file(ifn, iappend)
+{
+}
+
+
+intee::~intee()
+{
+ close();
+}
+
+
+void intee::doopen()
+{
+ infilter::doopen();
+ file.open();
+}
+
+
+void intee::doclose()
+{
+ file.close();
+ infilter::doclose();
+}
+
+
+void intee::dofilter()
+{
+ int count = stm->read(savebuf, savecount);
+ if (count > 0)
+ {
+ file.write(savebuf, count);
+ savebuf += count;
+ savecount -= count;
+ }
+}
+
+
+string intee::get_streamname()
+{
+ return "tee: " + file.get_filename();
+}
+
+
+}
diff --git a/source/ptypes/piobase.cxx b/source/ptypes/piobase.cxx
@@ -0,0 +1,388 @@
+/*
+ *
+ * C++ Portable Types Library (PTypes)
+ * Version 2.1.1 Released 27-Jun-2007
+ *
+ * Copyright (C) 2001-2007 Hovik Melikyan
+ *
+ * http://www.melikyan.com/ptypes/
+ *
+ */
+
+#include <errno.h>
+#include <limits.h>
+
+#ifdef WIN32
+# include <windows.h>
+#else
+# include <signal.h>
+# include <unistd.h>
+#endif
+
+#include "pstreams.h"
+
+
+namespace ptypes {
+
+
+/*
+
+Known UNIX error codes:
+
+EPERM 1 Not owner
+ENOENT 2 No such file or directory
+ESRCH 3 No such process
+EINTR 4 Interrupted system call
+EIO 5 I/O error
+ENXIO 6 No such device or address
+E2BIG 7 Argument list too long
+ENOEXEC 8 Exec format error
+EBADF 9 Bad file number
+ECHILD 10 No spawned processes
+EAGAIN 11 No more processes; not enough memory; maximum nesting level reached
+ENOMEM 12 Not enough memory
+EACCES 13 Permission denied
+EFAULT 14 Bad address
+ENOTBLK 15 Block device required
+EBUSY 16 Mount device busy
+EEXIST 17 File exists
+EXDEV 18 Cross-device link
+ENODEV 19 No such device
+ENOTDIR 20 Not a directory
+EISDIR 21 Is a directory
+EINVAL 22 Invalid argument
+ENFILE 23 File table overflow
+EMFILE 24 Too many open files
+ENOTTY 25 Not a teletype
+ETXTBSY 26 Text file busy
+EFBIG 27 File too large
+ENOSPC 28 No space left on device
+ESPIPE 29 Illegal seek
+EROFS 30 Read-only file system
+EMLINK 31 Too many links
+EPIPE 32 Broken pipe
+EDOM 33 Math argument
+ERANGE 34 Result too large
+EUCLEAN 35 File system needs cleaning
+EDEADLK 36 Resource deadlock would occur
+EDEADLOCK 36 Resource deadlock would occur
+
+*/
+
+
+#ifndef WIN32
+
+static class _io_init
+{
+public:
+ _io_init();
+} _io_init_inst;
+
+
+_io_init::_io_init()
+{
+ // We don't like broken pipes. PTypes will throw an exception instead.
+ signal(SIGPIPE, SIG_IGN);
+}
+
+#endif
+
+
+
+int ptdecl unixerrno()
+{
+#ifdef WIN32
+ switch(GetLastError())
+ {
+ case ERROR_FILE_NOT_FOUND:
+ case ERROR_PATH_NOT_FOUND: return ENOENT;
+ case ERROR_TOO_MANY_OPEN_FILES: return EMFILE;
+ case ERROR_ACCESS_DENIED:
+ case ERROR_SHARING_VIOLATION: return EACCES;
+ case ERROR_INVALID_HANDLE: return EBADF;
+ case ERROR_NOT_ENOUGH_MEMORY:
+ case ERROR_OUTOFMEMORY: return ENOMEM;
+ case ERROR_INVALID_DRIVE: return ENODEV;
+ case ERROR_WRITE_PROTECT: return EROFS;
+ case ERROR_FILE_EXISTS: return EEXIST;
+ case ERROR_BROKEN_PIPE: return EPIPE;
+ case ERROR_DISK_FULL: return ENOSPC;
+ case ERROR_SEEK_ON_DEVICE: return ESPIPE;
+ default: return EIO;
+ }
+#else
+ return errno;
+#endif
+}
+
+
+//
+// This function gives error messages for most frequently occurring
+// IO errors. If the function returns NULL a generic message
+// can be given, e.g. "I/O error". See also iobase::get_errormsg()
+//
+
+const char* ptdecl unixerrmsg(int code)
+{
+ switch(code)
+ {
+ case EBADF: return "Invalid file descriptor";
+ case ESPIPE: return "Can not seek on this device";
+ case ENOENT: return "No such file or directory";
+ case EMFILE: return "Too many open files";
+ case EACCES: return "Access denied";
+ case ENOMEM: return "Not enough memory";
+ case ENODEV: return "No such device";
+ case EROFS: return "Read-only file system";
+ case EEXIST: return "File already exists";
+ case ENOSPC: return "Disk full";
+ case EPIPE: return "Broken pipe";
+ case EFBIG: return "File too large";
+ default: return nil;
+ }
+}
+
+
+estream::estream(iobase* ierrstm, int icode, const char* imsg)
+ : exception(imsg), code(icode), errstm(ierrstm) {}
+
+
+estream::estream(iobase* ierrstm, int icode, const string& imsg)
+ : exception(imsg), code(icode), errstm(ierrstm) {}
+
+
+estream::~estream() {}
+
+
+int defbufsize = 8192;
+int stmbalance = 0;
+
+iobase::iobase(int ibufsize)
+ : component(), active(false), cancelled(false), eof(true),
+ handle(invhandle), abspos(0), bufsize(0), bufdata(nil), bufpos(0), bufend(0),
+ stmerrno(0), deferrormsg(), status(IO_CREATED), onstatus(nil)
+{
+ if (ibufsize < 0)
+ bufsize = defbufsize;
+ else
+ bufsize = ibufsize;
+}
+
+
+iobase::~iobase()
+{
+}
+
+
+void iobase::bufalloc()
+{
+ if (bufdata != nil)
+ fatal(CRIT_FIRST + 13, "(ptypes internal) invalid buffer allocation");
+ bufdata = (char*)memalloc(bufsize);
+}
+
+
+void iobase::buffree()
+{
+ bufclear();
+ memfree(bufdata);
+ bufdata = 0;
+}
+
+
+void iobase::chstat(int newstat)
+{
+ status = newstat;
+ if (onstatus != nil)
+ (*onstatus)(this, newstat);
+}
+
+
+void iobase::errstminactive()
+{
+ error(EIO, "Stream inactive");
+}
+
+
+void iobase::errbufrequired()
+{
+ fatal(CRIT_FIRST + 11, "Internal: buffer required");
+}
+
+
+int iobase::convertoffset(large offs)
+{
+ if (offs < 0 || offs > INT_MAX)
+ error(EFBIG, "File offset value too large");
+ return (int)offs;
+}
+
+
+void iobase::open()
+{
+ cancel();
+ chstat(IO_OPENING);
+ abspos = 0;
+ cancelled = false;
+ eof = false;
+ stmerrno = 0;
+ clear(deferrormsg);
+ active = true;
+ stmbalance++;
+ bufalloc();
+ doopen();
+ chstat(IO_OPENED);
+}
+
+
+void iobase::close()
+{
+ if (!active)
+ return;
+ stmbalance--;
+ try
+ {
+ if (bufdata != 0 && !cancelled)
+ flush();
+ doclose();
+ }
+ catch(estream* e)
+ {
+ delete e;
+ }
+ buffree();
+ active = false;
+ eof = true;
+ chstat(IO_CLOSED);
+}
+
+
+void iobase::cancel()
+{
+ cancelled = true;
+ close();
+}
+
+
+large iobase::seekx(large newpos, ioseekmode mode)
+{
+ if (!active)
+ errstminactive();
+ flush();
+ large ret = doseek(newpos, mode);
+ if (ret < 0)
+ error(ESPIPE, "Seek failed");
+ bufclear();
+ eof = false;
+ abspos = ret;
+ return ret;
+}
+
+
+void iobase::flush()
+{
+}
+
+
+large iobase::doseek(large newpos, ioseekmode mode)
+{
+ if (handle == invhandle)
+ {
+ error(ESPIPE, "Can't seek on this device");
+ return -1;
+ }
+#ifdef WIN32
+ static int wmode[3] = {FILE_BEGIN, FILE_CURRENT, FILE_END};
+ LARGE_INTEGER li;
+ li.QuadPart = newpos;
+ li.LowPart = SetFilePointer(HANDLE(handle), li.LowPart, &li.HighPart, wmode[mode]);
+ if (li.LowPart == INVALID_SET_FILE_POINTER && GetLastError() != NO_ERROR)
+ return -1;
+ return li.QuadPart;
+#else
+ static int umode[3] = {SEEK_SET, SEEK_CUR, SEEK_END};
+ return lseek(handle, newpos, umode[mode]);
+#endif
+}
+
+
+void iobase::doclose()
+{
+#ifdef WIN32
+ CloseHandle(HANDLE(pexchange(&handle, invhandle)));
+#else
+ ::close(pexchange(&handle, invhandle));
+#endif
+}
+
+
+void iobase::set_active(bool newval)
+{
+ if (newval != active)
+ {
+ if (newval)
+ open();
+ else
+ close();
+ }
+}
+
+
+void iobase::set_bufsize(int newval)
+{
+ if (active)
+ fatal(CRIT_FIRST + 12, "Cannot change buffer size while stream is active");
+ if (newval < 0)
+ bufsize = defbufsize;
+ else
+ bufsize = newval;
+}
+
+
+string iobase::get_errstmname()
+{
+ return get_streamname();
+}
+
+
+const char* iobase::uerrmsg(int code)
+{
+ return unixerrmsg(code);
+}
+
+
+int iobase::uerrno()
+{
+ return unixerrno();
+}
+
+
+string iobase::get_errormsg()
+{
+ string s = uerrmsg(stmerrno);
+ if (isempty(s))
+ s = deferrormsg;
+ if (pos('[', s) >= 0 && *(pconst(s) + length(s) - 1) == ']')
+ return s;
+ string e = get_errstmname();
+ if (isempty(e))
+ return s;
+ return s + " [" + e + ']';
+}
+
+
+#ifdef _MSC_VER
+// disable "unreachable code" warning for throw (known compiler bug)
+# pragma warning (disable: 4702)
+#endif
+
+void iobase::error(int code, const char* defmsg)
+{
+ eof = true;
+ stmerrno = code;
+ deferrormsg = defmsg;
+ throw new estream(this, code, get_errormsg());
+}
+
+
+}
diff --git a/source/ptypes/pipbase.cxx b/source/ptypes/pipbase.cxx
@@ -0,0 +1,421 @@
+/*
+ *
+ * C++ Portable Types Library (PTypes)
+ * Version 2.1.1 Released 27-Jun-2007
+ *
+ * Copyright (C) 2001-2007 Hovik Melikyan
+ *
+ * http://www.melikyan.com/ptypes/
+ *
+ */
+
+#ifdef WIN32
+# include <winsock2.h>
+#else
+# include <sys/time.h>
+# include <sys/types.h>
+# include <sys/socket.h>
+# include <netinet/in.h>
+# include <arpa/inet.h>
+# include <netdb.h>
+# include <unistd.h>
+# include <signal.h>
+# include <time.h>
+#endif
+
+#include <stdio.h> // for snprintf
+
+#include "pinet.h"
+#ifndef PTYPES_ST
+# include "pasync.h" // for mutex
+#endif
+
+
+namespace ptypes {
+
+//
+// reentrant gehostby*() mess
+//
+
+#if defined(PTYPES_ST)
+# define USE_GETHOSTBY
+#else
+# if defined(WIN32) || defined(__hpux) || defined(__ANDROID__)
+# define USE_GETHOSTBY
+# elif defined(__FreeBSD__) || defined(__DARWIN__)
+# define USE_GETIPNODEBY
+# elif defined(linux) || defined(__EMSCRIPTEN__)
+# define USE_GETHOSTBY_R6
+# elif defined(__NetBSD__) || defined(__OpenBSD__) || defined(__CYGWIN__)
+# define USE_LOCKED_GETHOSTBY
+# else // hopefully the Sun-style call will work on all other systems as well
+# define USE_GETHOSTBY_R5
+# endif
+#endif
+
+#define GETHOSTBY_BUF_SIZE 4096
+
+
+//
+// sockets init/startup
+//
+
+// usockerrno() is used in all socket classes anyway, so this module
+// along with the initialization code below will always be linked to
+// a networking program
+
+#ifdef WIN32
+
+static class _sock_init
+{
+public:
+ _sock_init();
+ ~_sock_init();
+} _init;
+
+
+_sock_init::_sock_init()
+{
+ WORD wVersionRequested;
+ WSADATA wsaData;
+ int err;
+
+ wVersionRequested = MAKEWORD(2, 0);
+
+ err = WSAStartup(wVersionRequested, &wsaData);
+ if ( err != 0 )
+ fatal(CRIT_FIRST + 50, "WinSock initialization failure");
+
+ if ( LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 0 )
+ fatal(CRIT_FIRST + 51, "WinSock version mismatch (2.0 or compatible required)");
+}
+
+
+_sock_init::~_sock_init()
+{
+ WSACleanup();
+}
+
+#endif
+
+
+
+//
+// internet address
+//
+
+ipaddress ipnone = uint(0xffffffff);
+ipaddress ipany = INADDR_ANY;
+ipaddress ipbcast = INADDR_BROADCAST;
+
+
+
+ipaddress::ipaddress(int a, int b, int c, int d)
+{
+ data[0] = uchar(a);
+ data[1] = uchar(b);
+ data[2] = uchar(c);
+ data[3] = uchar(d);
+}
+
+
+//
+// peer info
+//
+
+
+ippeerinfo::ippeerinfo()
+ : ip(ipnone), host(), port(0)
+{
+}
+
+
+ippeerinfo::ippeerinfo(ipaddress iip, const string& ihost, int iport)
+ : ip(iip), host(ihost), port(iport)
+{
+}
+
+
+ipaddress ippeerinfo::get_ip()
+{
+ if (ip == ipnone && !isempty(host))
+ {
+ ip = ulong(phostbyname(host));
+ if (ip == ipnone)
+ notfound();
+ }
+ return ip;
+}
+
+
+string ippeerinfo::get_host()
+{
+ if (!isempty(host))
+ return host;
+
+ if (ip == ipnone || ip == ipany || ip == ipbcast)
+ return nullstring;
+
+ host = phostbyaddr(ip);
+ if (isempty(host))
+ notfound();
+
+ return host;
+}
+
+
+void ippeerinfo::clear()
+{
+ ip = ipnone;
+ ptypes::clear(host);
+ port = 0;
+}
+
+
+string ippeerinfo::asstring(bool showport) const
+{
+ string t;
+ if (!isempty(host))
+ t = host;
+ else if (ip == ipany)
+ t = '*';
+ else if (ip == ipnone)
+ t = '-';
+ else
+ t = iptostring(ip);
+ if (showport && port != 0)
+ t += ':' + itostring(port);
+ return t;
+}
+
+
+//
+// internet utilities
+//
+
+int ptdecl usockerrno()
+{
+#ifdef WIN32
+ return WSAGetLastError();
+#else
+ return errno;
+#endif
+}
+
+//for VisualStudio2010
+#if (_MSC_VER>=1600)
+#define EPFNOSUPPORT WSAEPFNOSUPPORT
+#define EHOSTDOWN WSAEHOSTDOWN
+#endif
+
+const char* ptdecl usockerrmsg(int code)
+{
+ switch(code)
+ {
+ // only minimal set of most frequent/expressive errors; others go as "I/O error"
+ case ENOTSOCK: return "Invalid socket descriptor";
+ case EMSGSIZE: return "Message too long";
+ case ENOPROTOOPT:
+ case EPROTONOSUPPORT:
+ case EPFNOSUPPORT:
+ case EAFNOSUPPORT: return "Protocol or address family not supported";
+ case EADDRINUSE: return "Address already in use";
+ case EADDRNOTAVAIL: return "Address not available";
+ case ENETDOWN: return "Network is down";
+ case ENETUNREACH: return "Network is unreachable";
+ case ECONNRESET: return "Connection reset by peer";
+ case ETIMEDOUT: return "Operation timed out";
+ case ECONNREFUSED: return "Connection refused";
+ case EHOSTDOWN: return "Host is down";
+ case EHOSTUNREACH: return "No route to host";
+
+ // we always translate h_errno to ENOENT and simply show "host not found"
+ case ENOENT: return "Host not found";
+ default: return unixerrmsg(code);
+ }
+}
+
+
+string ptdecl iptostring(ipaddress ip)
+{
+ char buf[16];
+ snprintf(buf, sizeof(buf), "%d.%d.%d.%d",
+ uint(ip[0]), uint(ip[1]), uint(ip[2]), uint(ip[3]));
+ return string(buf);
+}
+
+
+#if defined(USE_LOCKED_GETHOSTBY)
+static mutex hplock;
+#endif
+
+
+ipaddress ptdecl phostbyname(const char* name)
+{
+ ipaddress ip;
+ hostent* hp;
+
+ if ((ip = ::inet_addr(name)) != ipnone)
+ {
+ if (ip[3] == 0) // network address?
+ return ipnone;
+ }
+ else
+ {
+#if defined(USE_GETHOSTBY)
+ if ((hp = ::gethostbyname(name)) != nil)
+#elif defined(USE_LOCKED_GETHOSTBY)
+ hplock.enter();
+ if ((hp = ::gethostbyname(name)) != nil)
+#elif defined(USE_GETIPNODEBY)
+ int herrno;
+ if ((hp = ::getipnodebyname(name, AF_INET, 0, &herrno)) != nil)
+#elif defined(USE_GETHOSTBY_R6)
+ int herrno;
+ hostent result;
+ char buf[GETHOSTBY_BUF_SIZE];
+ if ((::gethostbyname_r(name, &result, buf, sizeof(buf), &hp, &herrno) == 0) && hp)
+#elif defined(USE_GETHOSTBY_R5)
+ int herrno;
+ hostent result;
+ char buf[GETHOSTBY_BUF_SIZE];
+ if ((hp = ::gethostbyname_r(name, &result, buf, sizeof(buf), &herrno)) != nil)
+#endif
+ {
+ if (hp->h_addrtype == AF_INET)
+ memcpy(ip.data, hp->h_addr, sizeof(ip.data));
+#ifdef USE_GETIPNODEBY
+ freehostent(hp);
+#endif
+ }
+#if defined(USE_LOCKED_GETHOSTBY)
+ hplock.leave();
+#endif
+ }
+
+ return ip;
+}
+
+
+string ptdecl phostbyaddr(ipaddress ip)
+{
+ hostent* hp;
+ string r;
+
+#if defined(USE_GETHOSTBY)
+ if ((hp = ::gethostbyaddr(pconst(ip.data), sizeof(ip.data), AF_INET)) != nil)
+#elif defined(USE_LOCKED_GETHOSTBY)
+ hplock.enter();
+ if ((hp = ::gethostbyaddr(pconst(ip.data), sizeof(ip.data), AF_INET)) != nil)
+#elif defined(USE_GETIPNODEBY)
+ int herrno;
+ if ((hp = ::getipnodebyaddr(pconst(ip.data), sizeof(ip.data), AF_INET, &herrno)) != nil)
+#elif defined(USE_GETHOSTBY_R6)
+ int herrno;
+ hostent result;
+ char buf[GETHOSTBY_BUF_SIZE];
+ if ((::gethostbyaddr_r(pconst(ip.data), sizeof(ip.data), AF_INET, &result, buf, sizeof(buf), &hp, &herrno) == 0) && hp)
+#elif defined(USE_GETHOSTBY_R5)
+ int herrno;
+ hostent result;
+ char buf[GETHOSTBY_BUF_SIZE];
+ if ((hp = ::gethostbyaddr_r(pconst(ip.data), sizeof(ip.data), AF_INET, &result, buf, sizeof(buf), &herrno)) != nil)
+#endif
+ {
+ r = hp->h_name;
+#ifdef USE_GETIPNODEBY
+ freehostent(hp);
+#endif
+ }
+#if defined(USE_LOCKED_GETHOSTBY)
+ hplock.leave();
+#endif
+
+ return r;
+}
+
+
+string ptdecl phostcname(const char* name)
+{
+ hostent* hp;
+ string r;
+
+#if defined(USE_GETHOSTBY)
+ if ((hp = ::gethostbyname(name)) != nil)
+#elif defined(USE_LOCKED_GETHOSTBY)
+ hplock.enter();
+ if ((hp = ::gethostbyname(name)) != nil)
+#elif defined(USE_GETIPNODEBY)
+ int herrno;
+ if ((hp = ::getipnodebyname(name, AF_INET, 0, &herrno)) != nil)
+#elif defined(USE_GETHOSTBY_R6)
+ int herrno;
+ hostent result;
+ char buf[GETHOSTBY_BUF_SIZE];
+ if ((::gethostbyname_r(name, &result, buf, sizeof(buf), &hp, &herrno) == 0) && hp)
+#elif defined(USE_GETHOSTBY_R5)
+ int herrno;
+ hostent result;
+ char buf[GETHOSTBY_BUF_SIZE];
+ if ((hp = ::gethostbyname_r(name, &result, buf, sizeof(buf), &herrno)) != nil)
+#endif
+ {
+ r = hp->h_name;
+#ifdef USE_GETIPNODEBY
+ freehostent(hp);
+#endif
+ }
+#if defined(USE_LOCKED_GETHOSTBY)
+ hplock.leave();
+#endif
+
+ return r;
+}
+
+
+bool ptdecl psockwait(int handle, int timeout)
+{
+#ifdef _MSC_VER
+// disable "condition always true" warning caused by Microsoft's FD_SET macro
+# pragma warning (disable: 4127)
+#endif
+ if (handle < 0)
+ return false;
+ fd_set readfds;
+ FD_ZERO(&readfds);
+ FD_SET((uint)handle, &readfds);
+ timeval t;
+ t.tv_sec = timeout / 1000;
+ t.tv_usec = (timeout % 1000) * 1000;
+ return ::select(FD_SETSIZE, &readfds, nil, nil, (timeout < 0) ? nil : &t) > 0;
+}
+
+
+bool ptdecl psockname(int handle, ippeerinfo& p)
+{
+ sockaddr_in sa;
+ memset(&sa, 0, sizeof(sa));
+ psocklen addrlen = sizeof(sa);
+ if (getsockname(handle, (sockaddr*)&sa, &addrlen) != 0)
+ return false;
+ if (sa.sin_family != AF_INET)
+ return false;
+ p.ip = sa.sin_addr.s_addr;
+ p.port = ntohs(sa.sin_port);
+ return true;
+}
+
+
+#ifdef _MSC_VER
+// disable "unreachable code" warning for throw (known compiler bug)
+# pragma warning (disable: 4702)
+#endif
+
+void ippeerinfo::notfound()
+{
+ string t = usockerrmsg(ENOENT);
+ throw new estream(nil, ENOENT, t + " [" + asstring(false) + ']');
+}
+
+
+}
diff --git a/source/ptypes/pipmsg.cxx b/source/ptypes/pipmsg.cxx
@@ -0,0 +1,203 @@
+/*
+ *
+ * C++ Portable Types Library (PTypes)
+ * Version 2.1.1 Released 27-Jun-2007
+ *
+ * Copyright (C) 2001-2007 Hovik Melikyan
+ *
+ * http://www.melikyan.com/ptypes/
+ *
+ */
+
+#ifdef WIN32
+# include <winsock2.h>
+#else
+# include <sys/time.h>
+# include <sys/types.h>
+# include <sys/socket.h>
+# include <netinet/in.h>
+# include <arpa/inet.h>
+# include <netdb.h>
+# include <unistd.h>
+# include <time.h>
+#endif
+
+
+#include "pinet.h"
+
+
+namespace ptypes {
+
+
+//
+// ipmessage: IPv4 UDP message class
+//
+
+
+ipmessage::ipmessage()
+ : unknown(), ippeerinfo(ipnone, nullstring, 0), handle(invhandle) {}
+
+
+ipmessage::ipmessage(ipaddress iip, int iport)
+ : unknown(), ippeerinfo(iip, nullstring, iport), handle(invhandle) {}
+
+
+ipmessage::ipmessage(const char* ihost, int iport)
+ : unknown(), ippeerinfo(ipnone, ihost, iport), handle(invhandle) {}
+
+
+ipmessage::ipmessage(const string& ihost, int iport)
+ : unknown(), ippeerinfo(ipnone, ihost, iport), handle(invhandle) {}
+
+
+ipmessage::~ipmessage()
+{
+ close();
+}
+
+
+void ipmessage::set_ip(ipaddress iip)
+{
+ ip = iip;
+ ptypes::clear(host);
+}
+
+
+void ipmessage::set_host(const string& ihost)
+{
+ host = ihost;
+ ip = 0;
+}
+
+
+void ipmessage::set_host(const char* ihost)
+{
+ host = ihost;
+ ip = 0;
+}
+
+
+void ipmessage::set_port(int iport)
+{
+ port = iport;
+}
+
+
+ipaddress ipmessage::get_myip()
+{
+ ippeerinfo p;
+ if (!psockname(handle, p))
+ error(usockerrno(), "Couldn't get my IP");
+ return p.get_ip();
+}
+
+
+int ipmessage::get_myport()
+{
+ ippeerinfo p;
+ if (!psockname(handle, p))
+ error(usockerrno(), "Couldn't get my port number");
+ return p.get_port();
+}
+
+
+void ipmessage::close()
+{
+ if (handle != invhandle)
+ ::closesocket(pexchange(&handle, invhandle));
+}
+
+
+void ipmessage::open()
+{
+ close();
+ if ((handle = ::socket(AF_INET, SOCK_DGRAM, 0)) < 0)
+ error(usockerrno(), "Couldn't create socket");
+ // allow broadcasts
+ int one = 1;
+ if (::setsockopt(handle, SOL_SOCKET, SO_BROADCAST, (sockval_t)&one, sizeof(one)) != 0)
+ error(usockerrno(), "Couldn't enable broadcasts");
+ sockopt(handle);
+}
+
+
+void ipmessage::sockopt(int)
+{
+}
+
+
+bool ipmessage::waitfor(int timeout)
+{
+ return psockwait(handle, timeout);
+}
+
+
+void ipmessage::send(const char* buf, int count)
+{
+ if (handle == invhandle)
+ open();
+
+ sockaddr_in sa;
+ memset(&sa, 0, sizeof(sa));
+ sa.sin_family = AF_INET;
+ sa.sin_port = htons(ushort(get_port()));
+ sa.sin_addr.s_addr = get_ip();
+ if (sendto(handle, buf, count, 0, (sockaddr*)&sa, sizeof(sa)) < 0)
+ error(usockerrno(), "Couldn't write");
+}
+
+
+int ipmessage::receive(char* buf, int count, ipaddress& src)
+{
+ if (handle == invhandle)
+ error(EINVAL, "Couldn't read"); // must send() first
+
+ sockaddr_in sa;
+ psocklen fromlen = sizeof(sa);
+ int result = ::recvfrom(handle, buf, count, 0, (sockaddr*)&sa, &fromlen);
+ if (result < 0)
+ error(usockerrno(), "Couldn't read");
+ src = sa.sin_addr.s_addr;
+ return result;
+}
+
+
+int ipmessage::receive(char* buf, int count)
+{
+ ipaddress src;
+ return receive(buf, count, src);
+}
+
+
+string ipmessage::receive(int max, ipaddress& src)
+{
+ string result;
+ setlength(result, max);
+ int numread = receive(pchar(pconst(result)), max, src);
+ setlength(result, numread);
+ return result;
+}
+
+
+string ipmessage::receive(int max)
+{
+ ipaddress src;
+ return receive(max, src);
+}
+
+
+#ifdef _MSC_VER
+// disable "unreachable code" warning for throw (known compiler bug)
+# pragma warning (disable: 4702)
+#endif
+
+void ipmessage::error(int code, const char* msg)
+{
+ string s = usockerrmsg(code);
+ if (isempty(s))
+ s = msg;
+ throw new estream(nil, code, s + " [" + ippeerinfo::asstring(true) + ']');
+}
+
+
+}
diff --git a/source/ptypes/pipmsgsv.cxx b/source/ptypes/pipmsgsv.cxx
@@ -0,0 +1,140 @@
+/*
+ *
+ * C++ Portable Types Library (PTypes)
+ * Version 2.1.1 Released 27-Jun-2007
+ *
+ * Copyright (C) 2001-2007 Hovik Melikyan
+ *
+ * http://www.melikyan.com/ptypes/
+ *
+ */
+
+#ifdef WIN32
+# include <winsock2.h>
+#else
+# include <sys/time.h>
+# include <sys/types.h>
+# include <sys/socket.h>
+# include <netinet/in.h>
+# include <arpa/inet.h>
+# include <netdb.h>
+# include <unistd.h>
+# include <time.h>
+#endif
+
+
+#include "pinet.h"
+
+
+namespace ptypes {
+
+
+//
+// ipmsgserver: IPv4 UDP socket server
+//
+
+
+ipmsgserver::ipmsgserver()
+ : ipsvbase(SOCK_DGRAM), ippeerinfo(), handle(invhandle)
+{
+}
+
+
+ipmsgserver::~ipmsgserver()
+{
+ close();
+}
+
+
+void ipmsgserver::dobind(ipbindinfo* b)
+{
+ sockaddr_in sa;
+ memset(&sa, 0, sizeof(sa));
+ sa.sin_family = AF_INET;
+ sa.sin_port = htons(ushort(b->get_port()));
+ sa.sin_addr.s_addr = b->get_ip();
+ if (::bind(b->handle, (sockaddr*)&sa, sizeof(sa)) != 0)
+ error(*b, usockerrno(), "Couldn't bind address");
+}
+
+
+void ipmsgserver::close()
+{
+ if (!active)
+ return;
+ ipsvbase::close();
+ handle = invhandle;
+ ippeerinfo::clear();
+}
+
+
+bool ipmsgserver::poll(int i, int timeout)
+{
+ if (!active)
+ open();
+ return dopoll(&i, timeout);
+}
+
+
+int ipmsgserver::receive(char* buf, int count)
+{
+ if (!active)
+ open();
+ ippeerinfo::clear();
+
+ // determine which socket has pending data
+ int i = -1;
+ if (!dopoll(&i, -1))
+ error(*this, EINVAL, "Couldn't read");
+ ipbindinfo* b = (ipbindinfo*)addrlist[i];
+ handle = b->handle;
+
+ // read data
+ sockaddr_in sa;
+ psocklen len = sizeof(sa);
+ int result = ::recvfrom(handle, buf, count, 0, (sockaddr*)&sa, &len);
+ if (result < 0)
+ error(*b, usockerrno(), "Couldn't read");
+
+ // set up peer ip and port
+ ip = sa.sin_addr.s_addr;
+ port = ntohs(sa.sin_port);
+ return result;
+}
+
+
+string ipmsgserver::receive(int max)
+{
+ string result;
+ setlength(result, max);
+ int numread = receive(pchar(pconst(result)), max);
+ setlength(result, numread);
+ return result;
+}
+
+
+void ipmsgserver::send(const char* buf, int count)
+{
+ if (!active || handle == invhandle || ip == ipnone)
+ error(*this, EINVAL, "Couldn't write"); // must receive() first
+
+ sendto(buf, count, get_ip(), get_port());
+}
+
+
+void ipmsgserver::sendto(const char* buf, int count, ipaddress ip, int port)
+{
+ if (!active || handle == invhandle || ip == ipnone)
+ error(*this, EINVAL, "Couldn't write"); // must receive() first
+
+ sockaddr_in sa;
+ memset(&sa, 0, sizeof(sa));
+ sa.sin_family = AF_INET;
+ sa.sin_port = htons(ushort(port));
+ sa.sin_addr.s_addr = ip;
+ if (::sendto(handle, buf, count, 0, (sockaddr*)&sa, sizeof(sa)) < 0)
+ error(*this, usockerrno(), "Couldn't write");
+}
+
+
+}
diff --git a/source/ptypes/pipstm.cxx b/source/ptypes/pipstm.cxx
@@ -0,0 +1,248 @@
+/*
+ *
+ * C++ Portable Types Library (PTypes)
+ * Version 2.1.1 Released 27-Jun-2007
+ *
+ * Copyright (C) 2001-2007 Hovik Melikyan
+ *
+ * http://www.melikyan.com/ptypes/
+ *
+ */
+
+#ifdef WIN32
+# include <winsock2.h>
+#else
+# include <sys/time.h>
+# include <sys/types.h>
+# include <sys/socket.h>
+# include <netinet/in.h>
+# include <arpa/inet.h>
+# include <netdb.h>
+# include <unistd.h>
+# include <time.h>
+#endif
+
+
+#include "pinet.h"
+
+
+namespace ptypes {
+
+
+//
+// internet (ipv4) socket
+//
+
+
+ipstream::ipstream()
+ : fdxstm(), ippeerinfo(0, nullstring, 0), svsocket(invhandle) {}
+
+
+ipstream::ipstream(ipaddress iip, int iport)
+ : fdxstm(), ippeerinfo(iip, nullstring, iport), svsocket(invhandle) {}
+
+
+ipstream::ipstream(const char* ihost, int iport)
+ : fdxstm(), ippeerinfo(ipnone, ihost, iport), svsocket(invhandle) {}
+
+
+ipstream::ipstream(const string& ihost, int iport)
+ : fdxstm(), ippeerinfo(ipnone, ihost, iport), svsocket(invhandle) {}
+
+
+ipstream::~ipstream()
+{
+ cancel();
+}
+
+
+int ipstream::classid()
+{
+ return CLASS3_IPSTM;
+}
+
+
+int ipstream::uerrno()
+{
+ return usockerrno();
+}
+
+
+const char* ipstream::uerrmsg(int code)
+{
+ return usockerrmsg(code);
+}
+
+
+string ipstream::get_streamname()
+{
+ return ippeerinfo::asstring(true);
+}
+
+
+void ipstream::set_ip(ipaddress iip)
+{
+ close();
+ ip = iip;
+ ptypes::clear(host);
+}
+
+
+void ipstream::set_host(const string& ihost)
+{
+ close();
+ host = ihost;
+ ip = ipnone;
+}
+
+
+void ipstream::set_host(const char* ihost)
+{
+ close();
+ host = ihost;
+ ip = ipnone;
+}
+
+
+void ipstream::set_port(int iport)
+{
+ close();
+ port = iport;
+}
+
+
+void ipstream::doopen()
+{
+ sockaddr_in sa;
+ memset(&sa, 0, sizeof(sa));
+
+ if (svsocket != invhandle)
+ {
+ psocklen addrlen = sizeof(sa);
+
+ // open an active server socket and assign ip and host fields
+ chstat(IO_CONNECTING);
+
+ // the last parameter of accept() can be either int* or uint*
+ // depending on the target platform :(
+ if ((handle = ::accept(svsocket, (sockaddr*)&sa, &addrlen)) < 0)
+ error(uerrno(), "Couldn't create socket");
+ chstat(IO_CONNECTED);
+
+ if (sa.sin_family != AF_INET)
+ error(EAFNOSUPPORT, "Address family not supported");
+
+ ptypes::clear(host);
+ ip = sa.sin_addr.s_addr;
+ port = ntohs(sa.sin_port);
+ }
+
+ else
+ {
+ sa.sin_family = AF_INET;
+ sa.sin_port = htons(ushort(get_port()));
+
+ chstat(IO_RESOLVING);
+ sa.sin_addr.s_addr = get_ip(); // force to resolve the address if needed
+ chstat(IO_RESOLVED);
+
+ // open a client socket
+ if ((handle = ::socket(sa.sin_family, SOCK_STREAM, 0)) < 0)
+ error(uerrno(), "Couldn't create socket");
+
+ // a chance to set up extra socket options
+ sockopt(handle);
+
+ chstat(IO_CONNECTING);
+ if (::connect(handle, (sockaddr*)&sa, sizeof(sa)) < 0)
+ {
+ int e = uerrno();
+ closehandle();
+ error(e, "Couldn't connect to remote host");
+ }
+ chstat(IO_CONNECTED);
+ }
+}
+
+
+void ipstream::sockopt(int)
+{
+}
+
+
+void ipstream::closehandle()
+{
+ ::closesocket(pexchange(&handle, invhandle));
+}
+
+
+large ipstream::doseek(large, ioseekmode)
+{
+ return -1;
+}
+
+
+void ipstream::doclose()
+{
+ svsocket = invhandle;
+ if (!cancelled)
+ ::shutdown(handle, SHUT_RDWR);
+ closehandle();
+}
+
+
+#ifdef WIN32
+
+int ipstream::dorawread(char* buf, int count)
+{
+ int ret;
+ if ((ret = ::recv(handle, buf, count, 0)) == -1)
+ error(uerrno(), "Couldn't read");
+ return ret;
+}
+
+
+int ipstream::dorawwrite(const char* buf, int count)
+{
+ int ret;
+ if ((ret = ::send(handle, buf, count, 0)) == -1)
+ error(uerrno(), "Couldn't write");
+ return ret;
+}
+
+#endif
+
+
+bool ipstream::waitfor(int timeout)
+{
+ if (!active)
+ errstminactive();
+ if (bufsize > 0 && bufend > bufpos)
+ return true;
+ return psockwait(handle, timeout);
+}
+
+
+ipaddress ipstream::get_myip()
+{
+ if (!active)
+ errstminactive();
+ ippeerinfo p;
+ if (!psockname(handle, p))
+ error(uerrno(), "Couldn't get my IP");
+ return p.get_ip();
+}
+
+
+int ipstream::get_myport()
+{
+ if (!active)
+ errstminactive();
+ ippeerinfo p;
+ if (!psockname(handle, p))
+ error(uerrno(), "Couldn't get my port number");
+ return p.get_port();
+}
+
+
+}
diff --git a/source/ptypes/pipstmsv.cxx b/source/ptypes/pipstmsv.cxx
@@ -0,0 +1,101 @@
+/*
+ *
+ * C++ Portable Types Library (PTypes)
+ * Version 2.1.1 Released 27-Jun-2007
+ *
+ * Copyright (C) 2001-2007 Hovik Melikyan
+ *
+ * http://www.melikyan.com/ptypes/
+ *
+ */
+
+#ifdef WIN32
+# include <winsock2.h>
+#else
+# include <sys/time.h>
+# include <sys/types.h>
+# include <sys/socket.h>
+# include <netinet/in.h>
+# include <arpa/inet.h>
+# include <netdb.h>
+# include <unistd.h>
+# include <time.h>
+#endif
+
+
+#include "pinet.h"
+
+
+namespace ptypes {
+
+
+//
+// ipstmserver
+//
+
+
+ipstmserver::ipstmserver()
+ : ipsvbase(SOCK_STREAM)
+{
+}
+
+
+ipstmserver::~ipstmserver()
+{
+ close();
+}
+
+
+void ipstmserver::dobind(ipbindinfo* b)
+{
+#ifndef WIN32
+ // set SO_REAUSEADDR to true, unix only. on windows this option causes
+ // the previous owner of the socket to give up, which is not desirable
+ // in most cases, neither compatible with unix.
+ int one = 1;
+ if (::setsockopt(b->handle, SOL_SOCKET, SO_REUSEADDR,
+ (sockval_t)&one, sizeof(one)) != 0)
+ error(*b, usockerrno(), "Can't reuse local address");
+#endif
+
+ // set up sockaddr_in and try to bind it to the socket
+ sockaddr_in sa;
+ memset(&sa, 0, sizeof(sa));
+ sa.sin_family = AF_INET;
+ sa.sin_port = htons(ushort(b->get_port()));
+ sa.sin_addr.s_addr = b->get_ip();
+
+ if (::bind(b->handle, (sockaddr*)&sa, sizeof(sa)) != 0)
+ error(*b, usockerrno(), "Couldn't bind address");
+
+ if (::listen(b->handle, SOMAXCONN) != 0)
+ error(*b, usockerrno(), "Couldn't listen on socket");
+}
+
+
+bool ipstmserver::poll(int i, int timeout)
+{
+ if (!active)
+ open();
+ return dopoll(&i, timeout);
+}
+
+
+bool ipstmserver::serve(ipstream& client, int i, int timeout)
+{
+ if (!active)
+ open();
+
+ client.cancel();
+ if (dopoll(&i, timeout))
+ {
+ // connect the ipstream object to the client requesting the connection
+ client.svsocket = get_addr(i).handle;
+ client.open();
+ return true;
+ }
+ return false;
+}
+
+
+}
diff --git a/source/ptypes/pipsvbase.cxx b/source/ptypes/pipsvbase.cxx
@@ -0,0 +1,177 @@
+/*
+ *
+ * C++ Portable Types Library (PTypes)
+ * Version 2.1.1 Released 27-Jun-2007
+ *
+ * Copyright (C) 2001-2007 Hovik Melikyan
+ *
+ * http://www.melikyan.com/ptypes/
+ *
+ */
+
+#ifdef WIN32
+# include <winsock2.h>
+#else
+# include <sys/time.h>
+# include <sys/types.h>
+# include <sys/socket.h>
+# include <netinet/in.h>
+# include <arpa/inet.h>
+# include <netdb.h>
+# include <unistd.h>
+# include <time.h>
+#endif
+
+#include "pinet.h"
+
+
+namespace ptypes {
+
+
+//
+// ipbindinfo
+//
+
+
+ipbindinfo::ipbindinfo(ipaddress iip, const string& ihost, int iport)
+ : unknown(), ippeerinfo(iip, ihost, iport), handle(invhandle)
+{
+}
+
+
+ipbindinfo::~ipbindinfo()
+{
+}
+
+
+//
+// ipsvbase
+//
+
+
+ipsvbase::ipsvbase(int isocktype)
+ : socktype(isocktype), active(false), addrlist(true) {}
+
+
+ipsvbase::~ipsvbase()
+{
+ close();
+}
+
+
+void ipsvbase::error(ippeerinfo& p, int code, const char* defmsg)
+{
+ string msg = usockerrmsg(code);
+ if (isempty(msg))
+ msg = defmsg;
+ msg += " [" + p.asstring(true) + ']';
+ throw new estream(nil, code, msg);
+}
+
+
+int ipsvbase::bind(ipaddress ip, int port)
+{
+ close();
+ addrlist.add(new ipbindinfo(ip, nullstring, port));
+ return addrlist.get_count() - 1;
+}
+
+
+int ipsvbase::bindall(int port)
+{
+ close();
+ return bind(ipany, port);
+}
+
+
+void ipsvbase::clear()
+{
+ close();
+ addrlist.clear();
+}
+
+
+void ipsvbase::open()
+{
+ close();
+ if (addrlist.get_count() == 0)
+ fatal(CRIT_FIRST + 52, "No addresses specified to bind to");
+ active = true;
+ for (int i = 0; i < addrlist.get_count(); i++)
+ {
+ ipbindinfo* b = addrlist[i];
+ b->handle = ::socket(AF_INET, socktype, 0);
+ if (b->handle < 0)
+ error(*b, usockerrno(), "Couldn't create socket");
+ sockopt(b->handle);
+ dobind(b);
+ }
+}
+
+
+void ipsvbase::close()
+{
+ if (!active)
+ return;
+ for (int i = 0; i < addrlist.get_count(); i++)
+ {
+ ipbindinfo* b = addrlist[i];
+ ::closesocket(pexchange(&b->handle, invhandle));
+ }
+ active = false;
+}
+
+
+bool ipsvbase::dopoll(int* i, int timeout)
+{
+ fd_set set;
+ setupfds(&set, *i);
+ timeval t;
+ t.tv_sec = timeout / 1000;
+ t.tv_usec = (timeout % 1000) * 1000;
+ if (::select(FD_SETSIZE, &set, nil, nil, (timeout < 0) ? nil : &t) > 0)
+ {
+ if (*i >= 0)
+ return true;
+ // if the user selected -1 (all), find the socket which has a pending connection
+ // and assign it to i
+ for (int j = 0; j < addrlist.get_count(); j++)
+ if (FD_ISSET(uint(addrlist[j]->handle), &set))
+ {
+ *i = j;
+ return true;
+ }
+ }
+ return false;
+}
+
+
+void ipsvbase::setupfds(void* set, int i)
+{
+#ifdef _MSC_VER
+// disable "condition always true" warning caused by Microsoft's FD_SET macro
+# pragma warning (disable: 4127)
+#endif
+ FD_ZERO((fd_set*)set);
+ if (i >= 0)
+ {
+ int h = get_addr(i).handle;
+ if (h >= 0)
+ FD_SET((uint)h, (fd_set*)set);
+ }
+ else
+ for (i = 0; i < addrlist.get_count(); i++)
+ {
+ int h = addrlist[i]->handle;
+ if (h >= 0)
+ FD_SET((uint)h, (fd_set*)set);
+ }
+}
+
+
+void ipsvbase::sockopt(int)
+{
+}
+
+
+}
diff --git a/source/ptypes/pmd5.cxx b/source/ptypes/pmd5.cxx
@@ -0,0 +1,517 @@
+/*
+ *
+ * C++ Portable Types Library (PTypes)
+ * Version 2.1.1 Released 27-Jun-2007
+ *
+ * Copyright (C) 2001-2007 Hovik Melikyan
+ *
+ * http://www.melikyan.com/ptypes/
+ *
+ */
+
+/*
+ * Derived from L. Peter Deutsch's independent implementation
+ * of MD5 (RFC1321). The original copyright notice follows.
+ * This file is a concatenation of the original md5.h and
+ * md5.c and contains PTypes' MD5 wrapper class at the bottom.
+ */
+
+/*
+ Copyright (C) 1999, 2002 Aladdin Enterprises. All rights reserved.
+
+ This software is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this software.
+
+ Permission is granted to anyone to use this software for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+ 3. This notice may not be removed or altered from any source distribution.
+
+ L. Peter Deutsch
+ ghost@aladdin.com
+
+ */
+
+/*
+ Independent implementation of MD5 (RFC 1321).
+
+ This code implements the MD5 Algorithm defined in RFC 1321, whose
+ text is available at
+ http://www.ietf.org/rfc/rfc1321.txt
+ The code is derived from the text of the RFC, including the test suite
+ (section A.5) but excluding the rest of Appendix A. It does not include
+ any code or documentation that is identified in the RFC as being
+ copyrighted.
+
+ The original and principal author of md5.h is L. Peter Deutsch
+ <ghost@aladdin.com>. Other authors are noted in the change history
+ that follows (in reverse chronological order):
+
+ 2002-04-13 lpd Removed support for non-ANSI compilers; removed
+ references to Ghostscript; clarified derivation from RFC 1321;
+ now handles byte order either statically or dynamically.
+ 1999-11-04 lpd Edited comments slightly for automatic TOC extraction.
+ 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5);
+ added conditionalization for C++ compilation from Martin
+ Purschke <purschke@bnl.gov>.
+ 1999-05-03 lpd Original version.
+ */
+
+
+#include <string.h>
+
+#include "pstreams.h"
+
+
+namespace ptypes {
+
+
+//
+// --- md5.h ---------------------------------------------------------------
+//
+
+/*
+ * This package supports both compile-time and run-time determination of CPU
+ * byte order. If ARCH_IS_BIG_ENDIAN is defined as 0, the code will be
+ * compiled to run only on little-endian CPUs; if ARCH_IS_BIG_ENDIAN is
+ * defined as non-zero, the code will be compiled to run only on big-endian
+ * CPUs; if ARCH_IS_BIG_ENDIAN is not defined, the code will be compiled to
+ * run on either big- or little-endian CPUs, but will run slightly less
+ * efficiently on either one than if ARCH_IS_BIG_ENDIAN is defined.
+ */
+
+
+//
+// typedef unsigned char md5_byte_t; /* 8-bit byte */
+// typedef unsigned int md5_word_t; /* 32-bit word */
+//
+// /* Define the state of the MD5 Algorithm. */
+// typedef struct md5_state_s {
+// md5_word_t count[2]; /* message length in bits, lsw first */
+// md5_word_t abcd[4]; /* digest buffer */
+// md5_byte_t buf[64]; /* accumulate block */
+// } md5_state_t;
+//
+
+/* Initialize the algorithm. */
+void md5_init(md5_state_t *pms);
+
+/* Append a string to the message. */
+void md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes);
+
+/* Finish the message and return the digest. */
+void md5_finish(md5_state_t *pms, md5_byte_t digest[16]);
+
+
+//
+// --- md5.c ---------------------------------------------------------------
+//
+
+
+#undef BYTE_ORDER /* 1 = big-endian, -1 = little-endian, 0 = unknown */
+#ifdef ARCH_IS_BIG_ENDIAN
+# define BYTE_ORDER (ARCH_IS_BIG_ENDIAN ? 1 : -1)
+#else
+# define BYTE_ORDER 0
+#endif
+
+#define T_MASK ((md5_word_t)~0)
+#define T1 /* 0xd76aa478 */ (T_MASK ^ 0x28955b87)
+#define T2 /* 0xe8c7b756 */ (T_MASK ^ 0x173848a9)
+#define T3 0x242070db
+#define T4 /* 0xc1bdceee */ (T_MASK ^ 0x3e423111)
+#define T5 /* 0xf57c0faf */ (T_MASK ^ 0x0a83f050)
+#define T6 0x4787c62a
+#define T7 /* 0xa8304613 */ (T_MASK ^ 0x57cfb9ec)
+#define T8 /* 0xfd469501 */ (T_MASK ^ 0x02b96afe)
+#define T9 0x698098d8
+#define T10 /* 0x8b44f7af */ (T_MASK ^ 0x74bb0850)
+#define T11 /* 0xffff5bb1 */ (T_MASK ^ 0x0000a44e)
+#define T12 /* 0x895cd7be */ (T_MASK ^ 0x76a32841)
+#define T13 0x6b901122
+#define T14 /* 0xfd987193 */ (T_MASK ^ 0x02678e6c)
+#define T15 /* 0xa679438e */ (T_MASK ^ 0x5986bc71)
+#define T16 0x49b40821
+#define T17 /* 0xf61e2562 */ (T_MASK ^ 0x09e1da9d)
+#define T18 /* 0xc040b340 */ (T_MASK ^ 0x3fbf4cbf)
+#define T19 0x265e5a51
+#define T20 /* 0xe9b6c7aa */ (T_MASK ^ 0x16493855)
+#define T21 /* 0xd62f105d */ (T_MASK ^ 0x29d0efa2)
+#define T22 0x02441453
+#define T23 /* 0xd8a1e681 */ (T_MASK ^ 0x275e197e)
+#define T24 /* 0xe7d3fbc8 */ (T_MASK ^ 0x182c0437)
+#define T25 0x21e1cde6
+#define T26 /* 0xc33707d6 */ (T_MASK ^ 0x3cc8f829)
+#define T27 /* 0xf4d50d87 */ (T_MASK ^ 0x0b2af278)
+#define T28 0x455a14ed
+#define T29 /* 0xa9e3e905 */ (T_MASK ^ 0x561c16fa)
+#define T30 /* 0xfcefa3f8 */ (T_MASK ^ 0x03105c07)
+#define T31 0x676f02d9
+#define T32 /* 0x8d2a4c8a */ (T_MASK ^ 0x72d5b375)
+#define T33 /* 0xfffa3942 */ (T_MASK ^ 0x0005c6bd)
+#define T34 /* 0x8771f681 */ (T_MASK ^ 0x788e097e)
+#define T35 0x6d9d6122
+#define T36 /* 0xfde5380c */ (T_MASK ^ 0x021ac7f3)
+#define T37 /* 0xa4beea44 */ (T_MASK ^ 0x5b4115bb)
+#define T38 0x4bdecfa9
+#define T39 /* 0xf6bb4b60 */ (T_MASK ^ 0x0944b49f)
+#define T40 /* 0xbebfbc70 */ (T_MASK ^ 0x4140438f)
+#define T41 0x289b7ec6
+#define T42 /* 0xeaa127fa */ (T_MASK ^ 0x155ed805)
+#define T43 /* 0xd4ef3085 */ (T_MASK ^ 0x2b10cf7a)
+#define T44 0x04881d05
+#define T45 /* 0xd9d4d039 */ (T_MASK ^ 0x262b2fc6)
+#define T46 /* 0xe6db99e5 */ (T_MASK ^ 0x1924661a)
+#define T47 0x1fa27cf8
+#define T48 /* 0xc4ac5665 */ (T_MASK ^ 0x3b53a99a)
+#define T49 /* 0xf4292244 */ (T_MASK ^ 0x0bd6ddbb)
+#define T50 0x432aff97
+#define T51 /* 0xab9423a7 */ (T_MASK ^ 0x546bdc58)
+#define T52 /* 0xfc93a039 */ (T_MASK ^ 0x036c5fc6)
+#define T53 0x655b59c3
+#define T54 /* 0x8f0ccc92 */ (T_MASK ^ 0x70f3336d)
+#define T55 /* 0xffeff47d */ (T_MASK ^ 0x00100b82)
+#define T56 /* 0x85845dd1 */ (T_MASK ^ 0x7a7ba22e)
+#define T57 0x6fa87e4f
+#define T58 /* 0xfe2ce6e0 */ (T_MASK ^ 0x01d3191f)
+#define T59 /* 0xa3014314 */ (T_MASK ^ 0x5cfebceb)
+#define T60 0x4e0811a1
+#define T61 /* 0xf7537e82 */ (T_MASK ^ 0x08ac817d)
+#define T62 /* 0xbd3af235 */ (T_MASK ^ 0x42c50dca)
+#define T63 0x2ad7d2bb
+#define T64 /* 0xeb86d391 */ (T_MASK ^ 0x14792c6e)
+
+
+static void
+md5_process(md5_state_t *pms, const md5_byte_t *data /*[64]*/)
+{
+ md5_word_t
+ a = pms->abcd[0], b = pms->abcd[1],
+ c = pms->abcd[2], d = pms->abcd[3];
+ md5_word_t t;
+#if BYTE_ORDER > 0
+ /* Define storage only for big-endian CPUs. */
+ md5_word_t X[16];
+#else
+ /* Define storage for little-endian or both types of CPUs. */
+ md5_word_t xbuf[16];
+ const md5_word_t *X;
+#endif
+
+ {
+#if BYTE_ORDER == 0
+ /*
+ * Determine dynamically whether this is a big-endian or
+ * little-endian machine, since we can use a more efficient
+ * algorithm on the latter.
+ */
+ static const int w = 1;
+
+ if (*((const md5_byte_t *)&w)) /* dynamic little-endian */
+#endif
+#if BYTE_ORDER <= 0 /* little-endian */
+ {
+ /*
+ * On little-endian machines, we can process properly aligned
+ * data without copying it.
+ */
+ if (!((data - (const md5_byte_t *)0) & 3)) {
+ /* data are properly aligned */
+ X = (const md5_word_t *)data;
+ } else {
+ /* not aligned */
+ memcpy(xbuf, data, 64);
+ X = xbuf;
+ }
+ }
+#endif
+#if BYTE_ORDER == 0
+ else /* dynamic big-endian */
+#endif
+#if BYTE_ORDER >= 0 /* big-endian */
+ {
+ /*
+ * On big-endian machines, we must arrange the bytes in the
+ * right order.
+ */
+ const md5_byte_t *xp = data;
+ int i;
+
+# if BYTE_ORDER == 0
+ X = xbuf; /* (dynamic only) */
+# else
+# define xbuf X /* (static only) */
+# endif
+ for (i = 0; i < 16; ++i, xp += 4)
+ xbuf[i] = xp[0] + (xp[1] << 8) + (xp[2] << 16) + (xp[3] << 24);
+ }
+#endif
+ }
+
+#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32 - (n))))
+
+ /* Round 1. */
+ /* Let [abcd k s i] denote the operation
+ a = b + ((a + F(b,c,d) + X[k] + T[i]) <<< s). */
+#define F(x, y, z) (((x) & (y)) | (~(x) & (z)))
+#define SET(a, b, c, d, k, s, Ti)\
+ t = a + F(b,c,d) + X[k] + Ti;\
+ a = ROTATE_LEFT(t, s) + b
+ /* Do the following 16 operations. */
+ SET(a, b, c, d, 0, 7, T1);
+ SET(d, a, b, c, 1, 12, T2);
+ SET(c, d, a, b, 2, 17, T3);
+ SET(b, c, d, a, 3, 22, T4);
+ SET(a, b, c, d, 4, 7, T5);
+ SET(d, a, b, c, 5, 12, T6);
+ SET(c, d, a, b, 6, 17, T7);
+ SET(b, c, d, a, 7, 22, T8);
+ SET(a, b, c, d, 8, 7, T9);
+ SET(d, a, b, c, 9, 12, T10);
+ SET(c, d, a, b, 10, 17, T11);
+ SET(b, c, d, a, 11, 22, T12);
+ SET(a, b, c, d, 12, 7, T13);
+ SET(d, a, b, c, 13, 12, T14);
+ SET(c, d, a, b, 14, 17, T15);
+ SET(b, c, d, a, 15, 22, T16);
+#undef SET
+
+ /* Round 2. */
+ /* Let [abcd k s i] denote the operation
+ a = b + ((a + G(b,c,d) + X[k] + T[i]) <<< s). */
+#define G(x, y, z) (((x) & (z)) | ((y) & ~(z)))
+#define SET(a, b, c, d, k, s, Ti)\
+ t = a + G(b,c,d) + X[k] + Ti;\
+ a = ROTATE_LEFT(t, s) + b
+ /* Do the following 16 operations. */
+ SET(a, b, c, d, 1, 5, T17);
+ SET(d, a, b, c, 6, 9, T18);
+ SET(c, d, a, b, 11, 14, T19);
+ SET(b, c, d, a, 0, 20, T20);
+ SET(a, b, c, d, 5, 5, T21);
+ SET(d, a, b, c, 10, 9, T22);
+ SET(c, d, a, b, 15, 14, T23);
+ SET(b, c, d, a, 4, 20, T24);
+ SET(a, b, c, d, 9, 5, T25);
+ SET(d, a, b, c, 14, 9, T26);
+ SET(c, d, a, b, 3, 14, T27);
+ SET(b, c, d, a, 8, 20, T28);
+ SET(a, b, c, d, 13, 5, T29);
+ SET(d, a, b, c, 2, 9, T30);
+ SET(c, d, a, b, 7, 14, T31);
+ SET(b, c, d, a, 12, 20, T32);
+#undef SET
+
+ /* Round 3. */
+ /* Let [abcd k s t] denote the operation
+ a = b + ((a + H(b,c,d) + X[k] + T[i]) <<< s). */
+#define H(x, y, z) ((x) ^ (y) ^ (z))
+#define SET(a, b, c, d, k, s, Ti)\
+ t = a + H(b,c,d) + X[k] + Ti;\
+ a = ROTATE_LEFT(t, s) + b
+ /* Do the following 16 operations. */
+ SET(a, b, c, d, 5, 4, T33);
+ SET(d, a, b, c, 8, 11, T34);
+ SET(c, d, a, b, 11, 16, T35);
+ SET(b, c, d, a, 14, 23, T36);
+ SET(a, b, c, d, 1, 4, T37);
+ SET(d, a, b, c, 4, 11, T38);
+ SET(c, d, a, b, 7, 16, T39);
+ SET(b, c, d, a, 10, 23, T40);
+ SET(a, b, c, d, 13, 4, T41);
+ SET(d, a, b, c, 0, 11, T42);
+ SET(c, d, a, b, 3, 16, T43);
+ SET(b, c, d, a, 6, 23, T44);
+ SET(a, b, c, d, 9, 4, T45);
+ SET(d, a, b, c, 12, 11, T46);
+ SET(c, d, a, b, 15, 16, T47);
+ SET(b, c, d, a, 2, 23, T48);
+#undef SET
+
+ /* Round 4. */
+ /* Let [abcd k s t] denote the operation
+ a = b + ((a + I(b,c,d) + X[k] + T[i]) <<< s). */
+#define I(x, y, z) ((y) ^ ((x) | ~(z)))
+#define SET(a, b, c, d, k, s, Ti)\
+ t = a + I(b,c,d) + X[k] + Ti;\
+ a = ROTATE_LEFT(t, s) + b
+ /* Do the following 16 operations. */
+ SET(a, b, c, d, 0, 6, T49);
+ SET(d, a, b, c, 7, 10, T50);
+ SET(c, d, a, b, 14, 15, T51);
+ SET(b, c, d, a, 5, 21, T52);
+ SET(a, b, c, d, 12, 6, T53);
+ SET(d, a, b, c, 3, 10, T54);
+ SET(c, d, a, b, 10, 15, T55);
+ SET(b, c, d, a, 1, 21, T56);
+ SET(a, b, c, d, 8, 6, T57);
+ SET(d, a, b, c, 15, 10, T58);
+ SET(c, d, a, b, 6, 15, T59);
+ SET(b, c, d, a, 13, 21, T60);
+ SET(a, b, c, d, 4, 6, T61);
+ SET(d, a, b, c, 11, 10, T62);
+ SET(c, d, a, b, 2, 15, T63);
+ SET(b, c, d, a, 9, 21, T64);
+#undef SET
+
+ /* Then perform the following additions. (That is increment each
+ of the four registers by the value it had before this block
+ was started.) */
+ pms->abcd[0] += a;
+ pms->abcd[1] += b;
+ pms->abcd[2] += c;
+ pms->abcd[3] += d;
+}
+
+void
+md5_init(md5_state_t *pms)
+{
+ pms->count[0] = pms->count[1] = 0;
+ pms->abcd[0] = 0x67452301;
+ pms->abcd[1] = /*0xefcdab89*/ T_MASK ^ 0x10325476;
+ pms->abcd[2] = /*0x98badcfe*/ T_MASK ^ 0x67452301;
+ pms->abcd[3] = 0x10325476;
+}
+
+void
+md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes)
+{
+ const md5_byte_t *p = data;
+ int left = nbytes;
+ int offset = (pms->count[0] >> 3) & 63;
+ md5_word_t nbits = (md5_word_t)(nbytes << 3);
+
+ if (nbytes <= 0)
+ return;
+
+ /* Update the message length. */
+ pms->count[1] += nbytes >> 29;
+ pms->count[0] += nbits;
+ if (pms->count[0] < nbits)
+ pms->count[1]++;
+
+ /* Process an initial partial block. */
+ if (offset) {
+ int copy = (offset + nbytes > 64 ? 64 - offset : nbytes);
+
+ memcpy(pms->buf + offset, p, copy);
+ if (offset + copy < 64)
+ return;
+ p += copy;
+ left -= copy;
+ md5_process(pms, pms->buf);
+ }
+
+ /* Process full blocks. */
+ for (; left >= 64; p += 64, left -= 64)
+ md5_process(pms, p);
+
+ /* Process a final partial block. */
+ if (left)
+ memcpy(pms->buf, p, left);
+}
+
+void
+md5_finish(md5_state_t *pms, md5_byte_t digest[16])
+{
+ static const md5_byte_t pad[64] = {
+ 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ };
+ md5_byte_t data[8];
+ int i;
+
+ /* Save the length before padding. */
+ for (i = 0; i < 8; ++i)
+ data[i] = (md5_byte_t)(pms->count[i >> 2] >> ((i & 3) << 3));
+ /* Pad to 56 bytes mod 64. */
+ md5_append(pms, pad, ((55 - (pms->count[0] >> 3)) & 63) + 1);
+ /* Append the length. */
+ md5_append(pms, data, 8);
+ for (i = 0; i < 16; ++i)
+ digest[i] = (md5_byte_t)(pms->abcd[i >> 2] >> ((i & 3) << 3));
+}
+
+
+
+//
+// --- PTypes' wrapper class -----------------------------------------------
+//
+
+
+outmd5::outmd5(outstm* istm): outfilter(istm, 0)
+{
+ memset(&ctx, 0, sizeof ctx);
+ memset(digest, 0, sizeof digest);
+}
+
+
+outmd5::~outmd5()
+{
+ close();
+}
+
+
+void outmd5::doopen()
+{
+ outfilter::doopen();
+ memset(digest, 0, sizeof digest);
+ md5_init(&ctx);
+}
+
+
+void outmd5::doclose()
+{
+ md5_finish(&ctx, (unsigned char*)digest);
+ outfilter::doclose();
+}
+
+
+int outmd5::dorawwrite(const char* buf, int count)
+{
+ if (count > 0)
+ {
+ md5_append(&ctx, (const unsigned char*)buf, (unsigned)count);
+ if (stm != nil)
+ stm->write(buf, count);
+ return count;
+ }
+ else
+ return 0;
+}
+
+
+string outmd5::get_streamname()
+{
+ return "MD5";
+}
+
+
+string outmd5::get_digest()
+{
+ close();
+ string result;
+ // the first 120 bits are divided into 24-bit portions;
+ // each portion is represented with 4 characters from the base64 set
+ for (int i = 0; i <= 12; i += 3)
+ {
+ long v = (digest[i] << 16) | (digest[i + 1] << 8) | digest[i + 2];
+ result += itostring(large(v), 64, 4);
+ }
+ // the last byte is complemented with 4 zero bits to form
+ // the last two base64 characters
+ return result + itostring(large(digest[15] << 4), 64, 2);
+}
+
+
+}
diff --git a/source/ptypes/pmem.cxx b/source/ptypes/pmem.cxx
@@ -0,0 +1,86 @@
+/*
+ *
+ * C++ Portable Types Library (PTypes)
+ * Version 2.1.1 Released 27-Jun-2007
+ *
+ * Copyright (C) 2001-2007 Hovik Melikyan
+ *
+ * http://www.melikyan.com/ptypes/
+ *
+ */
+
+#include <stdlib.h>
+
+#include "pport.h"
+
+
+namespace ptypes {
+
+
+const int quant = 64;
+const int qmask = ~63;
+const int quant2 = 4096;
+const int qmask2 = ~4095;
+
+// dynamic reallocation policy for strings and lists
+
+int ptdecl memquantize(int a)
+{
+ if (a <= 16)
+ return 16;
+ if (a <= 32)
+ return 32;
+ else if (a <= 2048)
+ return (a + quant - 1) & qmask;
+ else
+ return (a + quant2 - 1) & qmask2;
+}
+
+
+void ptdecl memerror()
+{
+ fatal(CRIT_FIRST + 5, "Not enough memory");
+}
+
+
+void* ptdecl memalloc(uint a)
+{
+ if (a == 0)
+ return nil;
+ else
+ {
+ void* p = malloc(a);
+ if (p == nil)
+ memerror();
+ return p;
+ }
+}
+
+
+void* ptdecl memrealloc(void* p, uint a)
+{
+ if (a == 0)
+ {
+ memfree(p);
+ return nil;
+ }
+ else if (p == nil)
+ return memalloc(a);
+ else
+ {
+ p = realloc(p, a);
+ if (p == nil)
+ memerror();
+ return p;
+ }
+}
+
+
+void ptdecl memfree(void* p)
+{
+ if (p != nil)
+ free(p);
+}
+
+
+}
diff --git a/source/ptypes/pmsgq.cxx b/source/ptypes/pmsgq.cxx
@@ -0,0 +1,282 @@
+/*
+ *
+ * C++ Portable Types Library (PTypes)
+ * Version 2.1.1 Released 27-Jun-2007
+ *
+ * Copyright (C) 2001-2007 Hovik Melikyan
+ *
+ * http://www.melikyan.com/ptypes/
+ *
+ */
+
+#include "pasync.h"
+
+
+namespace ptypes {
+
+
+static void msgerror()
+{
+ fatal(CRIT_FIRST + 42, "Invalid message object");
+}
+
+
+message::message(int iid, pintptr iparam)
+ : next(nil), sync(nil), id(iid), param(iparam), result(0)
+{
+}
+
+
+message::~message()
+{
+}
+
+
+jobqueue::jobqueue(int ilimit)
+ : limit(ilimit), head(nil), tail(nil), qcount(0), sem(0), ovrsem(ilimit), qlock()
+{
+}
+
+
+jobqueue::~jobqueue()
+{
+ purgequeue();
+}
+
+
+bool jobqueue::enqueue(message* msg, int timeout)
+{
+ if (msg == nil)
+ msgerror();
+
+ if (!ovrsem.wait(timeout))
+ return false;
+ qlock.enter();
+ msg->next = nil;
+ if (head != nil)
+ head->next = msg;
+ head = msg;
+ if (tail == nil)
+ tail = msg;
+ qcount++;
+ qlock.leave();
+ sem.post();
+ return true;
+}
+
+
+bool jobqueue::push(message* msg, int timeout)
+{
+ if (msg == nil)
+ msgerror();
+
+ if (!ovrsem.wait(timeout))
+ return false;
+ qlock.enter();
+ msg->next = tail;
+ tail = msg;
+ if (head == nil)
+ head = msg;
+ qcount++;
+ qlock.leave();
+ sem.post();
+ return true;
+}
+
+
+message* jobqueue::dequeue(bool safe, int timeout)
+{
+ if (!sem.wait(timeout))
+ return nil;
+ if (safe)
+ qlock.enter();
+ message* msg = tail;
+ tail = msg->next;
+ qcount--;
+ if (tail == nil)
+ head = nil;
+ if (safe)
+ qlock.leave();
+ ovrsem.post();
+ return msg;
+}
+
+
+void jobqueue::purgequeue()
+{
+ qlock.enter();
+ while (get_count() > 0)
+ delete dequeue(false);
+ qlock.leave();
+}
+
+
+bool jobqueue::post(message* msg, int timeout)
+{
+ return enqueue(msg, timeout);
+}
+
+
+bool jobqueue::post(int id, pintptr param, int timeout)
+{
+ return post(new message(id, param), timeout);
+}
+
+
+bool jobqueue::posturgent(message* msg, int timeout)
+{
+ return push(msg, timeout);
+}
+
+
+bool jobqueue::posturgent(int id, pintptr param, int timeout)
+{
+ return posturgent(new message(id, param), timeout);
+}
+
+
+message* jobqueue::getmessage(int timeout)
+{
+ return dequeue(true, timeout);
+}
+
+
+msgqueue::msgqueue(int ilimit)
+ : jobqueue(ilimit), thrlock(), owner(0), quit(false)
+{
+}
+
+
+msgqueue::~msgqueue()
+{
+}
+
+
+void msgqueue::takeownership()
+{
+ if (owner != pthrself())
+ {
+ thrlock.enter(); // lock forever
+// if (owner != 0)
+// fatal(CRIT_FIRST + 45, "Ownership of the message queue already taken");
+ owner = pthrself();
+ }
+}
+
+
+pintptr msgqueue::finishmsg(message* msg)
+{
+ if (msg != nil)
+ {
+ pintptr result = msg->result;
+
+ // if the message was sent by send(),
+ // just signale the semaphore
+ if (msg->sync != nil)
+ msg->sync->post();
+
+ // otherwise finish it
+ else
+ delete msg;
+
+ return result;
+ }
+ else
+ return 0;
+}
+
+
+pintptr msgqueue::send(message* msg)
+{
+ if (msg == nil)
+ msgerror();
+
+ try
+ {
+ // if we are in the main thread,
+ // immediately handle the msg
+ if (pthrequal(owner))
+ handlemsg(msg);
+
+ // if this is called from a concurrent thread,
+ // sync through a semaphore
+ else
+ {
+ if (msg->sync != nil)
+ msgerror();
+ semaphore sync(0);
+ msg->sync = &sync;
+ push(msg);
+ msg->sync->wait();
+ msg->sync = 0;
+ }
+ }
+ catch (...)
+ {
+ finishmsg(msg);
+ throw;
+ }
+
+ return finishmsg(msg);
+}
+
+
+pintptr msgqueue::send(int id, pintptr param)
+{
+ return send(new message(id, param));
+}
+
+
+void msgqueue::processone()
+{
+ takeownership();
+ message* msg = dequeue();
+ try
+ {
+ handlemsg(msg);
+ }
+ catch(...)
+ {
+ finishmsg(msg);
+ throw;
+ }
+ finishmsg(msg);
+}
+
+
+void msgqueue::processmsgs()
+{
+ while (!quit && get_count() > 0)
+ processone();
+}
+
+
+void msgqueue::run()
+{
+ quit = false;
+ do
+ {
+ processone();
+ }
+ while (!quit);
+}
+
+
+void msgqueue::handlemsg(message* msg)
+{
+ msghandler(*msg);
+}
+
+
+void msgqueue::defhandler(message& msg)
+{
+ switch(msg.id)
+ {
+ case MSG_QUIT:
+ quit = true;
+ break;
+ }
+}
+
+
+}
diff --git a/source/ptypes/pmtxtable.cxx b/source/ptypes/pmtxtable.cxx
@@ -0,0 +1,42 @@
+/*
+ *
+ * C++ Portable Types Library (PTypes)
+ * Version 2.1.1 Released 27-Jun-2007
+ *
+ * Copyright (C) 2001-2007 Hovik Melikyan
+ *
+ * http://www.melikyan.com/ptypes/
+ *
+ */
+
+
+#include "pasync.h"
+
+
+namespace ptypes {
+
+
+pmemlock _mtxtable[_MUTEX_HASH_SIZE] // currently 29
+#ifndef WIN32
+ = {
+ _MTX_INIT, _MTX_INIT,
+ _MTX_INIT, _MTX_INIT,
+ _MTX_INIT, _MTX_INIT,
+ _MTX_INIT, _MTX_INIT,
+ _MTX_INIT, _MTX_INIT,
+ _MTX_INIT, _MTX_INIT,
+ _MTX_INIT, _MTX_INIT,
+ _MTX_INIT, _MTX_INIT,
+ _MTX_INIT, _MTX_INIT,
+ _MTX_INIT, _MTX_INIT,
+ _MTX_INIT, _MTX_INIT,
+ _MTX_INIT, _MTX_INIT,
+ _MTX_INIT, _MTX_INIT,
+ _MTX_INIT, _MTX_INIT,
+ _MTX_INIT
+}
+#endif
+;
+
+
+}
diff --git a/source/ptypes/pnpipe.cxx b/source/ptypes/pnpipe.cxx
@@ -0,0 +1,290 @@
+/*
+ *
+ * C++ Portable Types Library (PTypes)
+ * Version 2.1.1 Released 27-Jun-2007
+ *
+ * Copyright (C) 2001-2007 Hovik Melikyan
+ *
+ * http://www.melikyan.com/ptypes/
+ *
+ */
+
+#ifdef WIN32
+# include <windows.h>
+#else
+# include <sys/types.h>
+# include <sys/socket.h>
+# include <sys/un.h>
+#endif
+
+#include "pstreams.h"
+
+
+namespace ptypes {
+
+
+#ifdef WIN32
+
+string ptdecl namedpipe::realpipename(const string& pipename, const string& svrname)
+{
+ if (isempty(pipename))
+ return nullstring;
+ string realname = pipename;
+ if (*pconst(pipename) == '/')
+ {
+ int i = rpos('/', realname);
+ del(realname, 0, i + 1);
+ }
+ string s;
+ if (isempty(svrname))
+ s = '.';
+ else
+ s = svrname;
+ return "\\\\" + s + "\\pipe\\" + realname;
+}
+
+
+#else
+
+string ptdecl namedpipe::realpipename(const string& pipename)
+{
+ if (isempty(pipename))
+ return nullstring;
+ if (*pconst(pipename) == '/')
+ return pipename;
+ else
+ return DEF_NAMED_PIPES_DIR + pipename;
+}
+
+
+bool namedpipe::setupsockaddr(const string& pipename, void* isa)
+{
+ sockaddr_un* sa = (sockaddr_un*)isa;
+ memset(sa, 0, sizeof(sockaddr_un));
+ sa->sun_family = AF_UNIX;
+#ifdef __FreeBSD__
+ sa->sun_len = length(pipename);
+#endif
+
+ // copy the path name into the sockaddr structure, 108 chars max (?)
+ if (length(pipename) + 1 > (int)sizeof(sa->sun_path))
+ return false;
+ strcpy(sa->sun_path, pipename);
+ return true;
+}
+
+#endif
+
+
+namedpipe::namedpipe()
+ : fdxstm(), pipename(), svhandle(invhandle)
+{
+ initovr();
+}
+
+
+namedpipe::namedpipe(const string& ipipename)
+ : fdxstm(), pipename(), svhandle(invhandle)
+{
+ pipename = realpipename(ipipename);
+ initovr();
+}
+
+
+#ifdef WIN32
+
+namedpipe::namedpipe(const string& ipipename, const string& servername)
+ : fdxstm(), pipename(), svhandle(invhandle)
+{
+ pipename = realpipename(ipipename, servername);
+ initovr();
+}
+
+
+void namedpipe::initovr()
+{
+ ovr.hEvent = CreateEvent(0, false, false, 0);
+}
+
+
+#endif
+
+
+namedpipe::~namedpipe()
+{
+ cancel();
+#ifdef WIN32
+ CloseHandle(ovr.hEvent);
+#endif
+}
+
+
+int namedpipe::classid()
+{
+ return CLASS3_NPIPE;
+}
+
+
+string namedpipe::get_streamname()
+{
+ return pipename;
+}
+
+
+void namedpipe::set_pipename(const string& newvalue)
+{
+ close();
+ pipename = realpipename(newvalue);
+}
+
+
+void namedpipe::set_pipename(const char* newvalue)
+{
+ close();
+ pipename = realpipename(newvalue);
+}
+
+
+large namedpipe::doseek(large, ioseekmode)
+{
+ return -1;
+}
+
+
+void namedpipe::doopen()
+{
+
+#ifdef WIN32
+
+ if (svhandle != invhandle)
+ handle = svhandle;
+ else
+ {
+ int tries = DEF_PIPE_OPEN_RETRY;
+ int delay = DEF_PIPE_OPEN_TIMEOUT / 2;
+retry:
+ SECURITY_ATTRIBUTES sa;
+ sa.nLength = sizeof(SECURITY_ATTRIBUTES);
+ sa.lpSecurityDescriptor = NULL;
+ sa.bInheritHandle = TRUE;
+
+ handle = int(CreateFileA(pipename, GENERIC_READ | GENERIC_WRITE,
+ 0, &sa, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0));
+ if (handle == invhandle)
+ {
+ if (GetLastError() == ERROR_PIPE_BUSY)
+ {
+ if (--tries > 0)
+ {
+ delay *= 2;
+ Sleep(delay);
+ goto retry;
+ }
+ else
+ error(EIO, "Pipe busy");
+ }
+ else
+ error(uerrno(), "Couldn't open named pipe");
+ }
+ }
+
+#else
+ if (svhandle != invhandle)
+ {
+ if ((handle = ::accept(svhandle, 0, 0)) < 0)
+ error(uerrno(), "Couldn't create local socket");
+ }
+
+ else
+ {
+ sockaddr_un sa;
+ if (!setupsockaddr(pipename, &sa))
+ error(ERANGE, "Socket name too long");
+
+ // cteate a client socket
+ if ((handle = ::socket(sa.sun_family, SOCK_STREAM, 0)) < 0)
+ error(uerrno(), "Couldn't create local socket");
+
+ // ... and connect to the local socket
+ if (::connect(handle, (sockaddr*)&sa, sizeof(sa)) < 0)
+ {
+ int e = uerrno();
+ doclose();
+ error(e, "Couldn't connect to local socket");
+ }
+ }
+
+#endif
+}
+
+
+#ifdef WIN32
+
+int namedpipe::dorawread(char* buf, int count)
+{
+ unsigned long ret = uint(-1);
+ ovr.Offset = 0;
+ ovr.OffsetHigh = 0;
+ if (!ReadFile(HANDLE(handle), buf, count, &ret, &ovr))
+ {
+ if (GetLastError() == ERROR_IO_PENDING)
+ {
+ if (WaitForSingleObject(ovr.hEvent, DEF_PIPE_TIMEOUT) == WAIT_TIMEOUT)
+ error(EIO, "Timed out");
+ if (!GetOverlappedResult(HANDLE(handle), &ovr, &ret, false))
+ error(uerrno(), "Couldn't read");
+ }
+ else
+ error(uerrno(), "Couldn't read");
+ }
+ return ret;
+}
+
+
+int namedpipe::dorawwrite(const char* buf, int count)
+{
+ unsigned long ret = uint(-1);
+ ovr.Offset = 0;
+ ovr.OffsetHigh = 0;
+ if (!WriteFile(HANDLE(handle), buf, count, &ret, &ovr))
+ {
+ if (GetLastError() == ERROR_IO_PENDING)
+ {
+ if (WaitForSingleObject(ovr.hEvent, DEF_PIPE_TIMEOUT) == WAIT_TIMEOUT)
+ error(EIO, "Timed out");
+ if (!GetOverlappedResult(HANDLE(handle), &ovr, &ret, false))
+ error(uerrno(), "Couldn't write");
+ }
+ else
+ error(uerrno(), "Couldn't write");
+ }
+ return ret;
+}
+
+
+#endif
+
+
+void namedpipe::doclose()
+{
+#ifdef WIN32
+ if (svhandle != invhandle)
+ DisconnectNamedPipe(HANDLE(handle));
+#endif
+ svhandle = invhandle;
+ fdxstm::doclose();
+}
+
+
+void namedpipe::flush()
+{
+#ifdef WIN32
+ if (!cancelled)
+ FlushFileBuffers(HANDLE(handle));
+#endif
+ fdxstm::flush();
+}
+
+
+
+}
diff --git a/source/ptypes/pnpserver.cxx b/source/ptypes/pnpserver.cxx
@@ -0,0 +1,185 @@
+/*
+ *
+ * C++ Portable Types Library (PTypes)
+ * Version 2.1.1 Released 27-Jun-2007
+ *
+ * Copyright (C) 2001-2007 Hovik Melikyan
+ *
+ * http://www.melikyan.com/ptypes/
+ *
+ */
+
+#ifdef WIN32
+# include <windows.h>
+#else
+# include <sys/time.h>
+# include <sys/types.h>
+# include <sys/socket.h>
+# include <sys/un.h>
+# include <unistd.h>
+#endif
+
+#include "pstreams.h"
+
+
+namespace ptypes {
+
+
+npserver::npserver(const string& ipipename)
+ : pipename(), handle(invhandle), active(false)
+{
+ pipename = namedpipe::realpipename(ipipename);
+}
+
+
+npserver::~npserver()
+{
+ close();
+}
+
+
+void npserver::error(int code, const char* defmsg)
+{
+ string msg = unixerrmsg(code);
+ if (isempty(msg))
+ msg = defmsg;
+ msg += " [" + pipename + ']';
+ throw new estream(nil, code, msg);
+}
+
+
+#ifdef WIN32
+
+void npserver::openinst()
+{
+ // called once at startup and then again, after
+ // each client connection. strange logic, to say the least...
+ SECURITY_ATTRIBUTES sa;
+ sa.nLength = sizeof(SECURITY_ATTRIBUTES);
+ sa.lpSecurityDescriptor = NULL;
+ sa.bInheritHandle = TRUE;
+
+ handle = (int)CreateNamedPipeA(pipename,
+ PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
+ PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT,
+ PIPE_UNLIMITED_INSTANCES,
+ DEF_PIPE_SYSTEM_BUF_SIZE, DEF_PIPE_SYSTEM_BUF_SIZE,
+ DEF_PIPE_TIMEOUT, &sa);
+
+ if (handle == invhandle)
+ error(unixerrno(), "Couldn't create");
+}
+
+
+void npserver::closeinst()
+{
+ CloseHandle(HANDLE(pexchange(&handle, invhandle)));
+}
+
+#endif
+
+
+void npserver::open()
+{
+ close();
+
+#ifdef WIN32
+
+ openinst();
+
+#else
+
+ sockaddr_un sa;
+ if (!namedpipe::setupsockaddr(pipename, &sa))
+ error(ERANGE, "Socket name too long");
+
+ if ((handle = ::socket(sa.sun_family, SOCK_STREAM, 0)) < 0)
+ error(unixerrno(), "Couldn't create local socket");
+
+ unlink(pipename);
+ if (::bind(handle, (sockaddr*)&sa, sizeof(sa)) != 0)
+ error(unixerrno(), "Couldn't bind local socket");
+
+ if (::listen(handle, SOMAXCONN) != 0)
+ error(unixerrno(), "Couldn't listen on local socket");
+
+#endif
+
+ active = true;
+}
+
+
+void npserver::close()
+{
+ if (active)
+ {
+ active = false;
+#ifdef WIN32
+ closeinst();
+#else
+ ::close(pexchange(&handle, invhandle));
+ unlink(pipename);
+#endif
+ }
+}
+
+
+bool npserver::serve(namedpipe& client, int timeout)
+{
+ if (!active)
+ open();
+
+ client.cancel();
+
+#ifdef WIN32
+
+ client.ovr.Offset = 0;
+ client.ovr.OffsetHigh = 0;
+ bool result = ConnectNamedPipe(HANDLE(handle), &client.ovr) ?
+ true : (GetLastError() == ERROR_PIPE_CONNECTED);
+
+ if (!result && GetLastError() == ERROR_IO_PENDING)
+ {
+ if (WaitForSingleObject(client.ovr.hEvent, timeout) == WAIT_TIMEOUT)
+ return false;
+ unsigned long ret;
+ if (!GetOverlappedResult(HANDLE(handle), &client.ovr, &ret, false))
+ error(unixerrno(), "Couldn't read");
+ result = true;
+ }
+
+ if (result)
+ {
+ client.svhandle = handle;
+ client.pipename = pipename;
+ openinst();
+ client.open();
+ return true;
+ }
+ else
+ error(unixerrno(), "Couldn't connect to client");
+
+ return false;
+
+#else
+
+ fd_set set;
+ FD_ZERO(&set);
+ FD_SET((uint)handle, &set);
+ timeval t;
+ t.tv_sec = timeout / 1000;
+ t.tv_usec = (timeout % 1000) * 1000;
+ if (::select(FD_SETSIZE, &set, nil, nil, (timeout < 0) ? nil : &t) > 0)
+ {
+ client.svhandle = handle;
+ client.pipename = pipename;
+ client.open();
+ return true;
+ }
+ return false;
+
+#endif
+}
+
+
+}
diff --git a/source/ptypes/pobjlist.cxx b/source/ptypes/pobjlist.cxx
@@ -0,0 +1,156 @@
+/*
+ *
+ * C++ Portable Types Library (PTypes)
+ * Version 2.1.1 Released 27-Jun-2007
+ *
+ * Copyright (C) 2001-2007 Hovik Melikyan
+ *
+ * http://www.melikyan.com/ptypes/
+ *
+ */
+
+#include "ptypes.h"
+
+
+namespace ptypes {
+
+
+_objlist::_objlist()
+ : tpodlist<void*,true>()
+{
+ memset(&config, 0, sizeof(config));
+}
+
+
+_objlist::_objlist(bool ownobjects)
+ : tpodlist<void*,true>()
+{
+ memset(&config, 0, sizeof(config));
+ config.ownobjects = ownobjects;
+}
+
+
+_objlist::~_objlist()
+{
+}
+
+
+void _objlist::dofree(void*)
+{
+ fatal(CRIT_FIRST + 38, "ptrlist::dofree() not defined");
+}
+
+
+int _objlist::compare(const void*, const void*) const
+{
+ fatal(CRIT_FIRST + 38, "ptrlist::compare() not defined");
+ return 0;
+}
+
+
+void _objlist::dofree(int index, int num)
+{
+ void** p = (void**)list + index;
+ while (--num >= 0)
+ dofree(*p++);
+}
+
+
+void _objlist::doput(int index, void* obj)
+{
+ void** p = (void**)list + index;
+ if (config.ownobjects)
+ dofree(*p);
+ *p = obj;
+}
+
+
+void _objlist::dodel(int index)
+{
+ if (config.ownobjects)
+ dofree(doget(index));
+ tpodlist<void*, true>::dodel(index);
+}
+
+
+void _objlist::dodel(int index, int delcount)
+{
+ if (config.ownobjects)
+ {
+ if (index + delcount > count)
+ delcount = count - index;
+ dofree(index, delcount);
+ }
+ tpodlist<void*, true>::dodel(index, delcount);
+}
+
+
+void _objlist::set_count(int newcount)
+{
+ if (newcount < count && config.ownobjects)
+ {
+ if (newcount < 0)
+ newcount = 0;
+ dofree(newcount, count - newcount);
+ }
+ _podlist::set_count(newcount, true);
+}
+
+
+void* _objlist::dopop()
+{
+ void* t = doget(--count);
+ if (count == 0)
+ set_capacity(0);
+ return t;
+}
+
+
+bool _objlist::search(const void* key, int& index) const
+{
+ int l, h, i, c;
+ bool ret = false;
+ l = 0;
+ h = count - 1;
+ while (l <= h)
+ {
+ i = (l + h) / 2;
+ c = compare(key, doget(i));
+ if (c > 0)
+ l = i + 1;
+ else
+ {
+ h = i - 1;
+ if (c == 0)
+ {
+ ret = true;
+ if (!config.duplicates)
+ l = i;
+ }
+ }
+ }
+ index = l;
+ return ret;
+}
+
+
+int _objlist::indexof(void* obj) const
+{
+ for (int i = 0; i < count; i++)
+ if (doget(i) == obj)
+ return i;
+ return -1;
+}
+
+
+#ifdef PTYPES19_COMPAT
+
+objlist::objlist(bool ownobjects): tobjlist<unknown>(ownobjects) {}
+
+objlist::~objlist() {}
+
+#endif
+
+
+}
+
diff --git a/source/ptypes/poutfile.cxx b/source/ptypes/poutfile.cxx
@@ -0,0 +1,111 @@
+/*
+ *
+ * C++ Portable Types Library (PTypes)
+ * Version 2.1.1 Released 27-Jun-2007
+ *
+ * Copyright (C) 2001-2007 Hovik Melikyan
+ *
+ * http://www.melikyan.com/ptypes/
+ *
+ */
+
+#include "errno.h"
+
+#ifdef WIN32
+# include <windows.h>
+#else
+# include <fcntl.h>
+# include <unistd.h>
+#endif
+
+#include "pstreams.h"
+
+
+namespace ptypes {
+
+
+// *BSD hack
+#ifndef O_LARGEFILE
+# define O_LARGEFILE 0
+#endif
+
+
+outfile::outfile()
+ : outstm(), filename(), syshandle(invhandle), peerhandle(invhandle),
+ umode(0644), append(false) {}
+
+
+outfile::outfile(const char* ifn, bool iappend)
+ : outstm(), filename(ifn), syshandle(invhandle), peerhandle(invhandle),
+ umode(0644), append(iappend) {}
+
+
+outfile::outfile(string const& ifn, bool iappend)
+ : outstm(), filename(ifn), syshandle(invhandle), peerhandle(invhandle),
+ umode(0644), append(iappend) {}
+
+
+outfile::~outfile()
+{
+ close();
+}
+
+
+int outfile::classid()
+{
+ return CLASS2_OUTFILE;
+}
+
+
+string outfile::get_streamname()
+{
+ return filename;
+}
+
+
+void outfile::doopen()
+{
+ if (syshandle != invhandle)
+ handle = syshandle;
+ else
+ {
+#ifdef WIN32
+ SECURITY_ATTRIBUTES sa;
+ sa.nLength = sizeof(SECURITY_ATTRIBUTES);
+ sa.lpSecurityDescriptor = NULL;
+ sa.bInheritHandle = TRUE;
+
+ handle = int(CreateFileA(filename, GENERIC_WRITE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE, &sa,
+ (append ? OPEN_ALWAYS : CREATE_ALWAYS), 0, 0));
+#else
+ handle = ::open(filename,
+ O_WRONLY | O_CREAT | O_LARGEFILE | (append ? 0 : O_TRUNC), umode);
+#endif
+ if (handle == invhandle)
+ error(uerrno(), "Couldn't open");
+ if (append)
+ if (doseek(0, IO_END) == -1)
+ error(uerrno(), "Couldn't seek to end of file");
+ }
+}
+
+
+void outfile::flush()
+{
+ outstm::flush();
+#ifdef WIN32
+ FlushFileBuffers(HANDLE(handle));
+#endif
+}
+
+
+void outfile::doclose()
+{
+ outstm::doclose();
+ syshandle = invhandle;
+ peerhandle = invhandle;
+}
+
+
+}
diff --git a/source/ptypes/poutfilter.cxx b/source/ptypes/poutfilter.cxx
@@ -0,0 +1,77 @@
+/*
+ *
+ * C++ Portable Types Library (PTypes)
+ * Version 2.1.1 Released 27-Jun-2007
+ *
+ * Copyright (C) 2001-2007 Hovik Melikyan
+ *
+ * http://www.melikyan.com/ptypes/
+ *
+ */
+
+#include <string.h>
+
+#include "pstreams.h"
+
+
+namespace ptypes {
+
+
+outfilter::outfilter(outstm* istm, int ibufsize)
+: outstm(false, ibufsize), stm(istm)
+{
+ if (stm != nil)
+ stm->addnotification(this);
+}
+
+
+outfilter::~outfilter()
+{
+ if (stm != nil)
+ stm->delnotification(this);
+}
+
+
+void outfilter::freenotify(component* sender)
+{
+ if (sender == stm)
+ {
+ stm = nil;
+ close();
+ }
+}
+
+
+void outfilter::doopen()
+{
+ if (stm != nil && !stm->get_active())
+ stm->open();
+}
+
+
+void outfilter::doclose()
+{
+}
+
+
+string outfilter::get_errstmname()
+{
+ if (stm == nil)
+ return get_streamname();
+ else
+ return get_streamname() + ": " + stm->get_errstmname();
+}
+
+
+void outfilter::set_stm(outstm* istm)
+{
+ close();
+ if (stm != nil)
+ stm->delnotification(this);
+ stm = istm;
+ if (stm != nil)
+ stm->addnotification(this);
+}
+
+
+}
diff --git a/source/ptypes/poutmem.cxx b/source/ptypes/poutmem.cxx
@@ -0,0 +1,104 @@
+/*
+ *
+ * C++ Portable Types Library (PTypes)
+ * Version 2.1.1 Released 27-Jun-2007
+ *
+ * Copyright (C) 2001-2007 Hovik Melikyan
+ *
+ * http://www.melikyan.com/ptypes/
+ *
+ */
+
+#include "pstreams.h"
+
+
+namespace ptypes {
+
+
+outmemory::outmemory(int ilimit)
+ : outstm(false, 0), mem(), limit(ilimit)
+{
+}
+
+
+outmemory::~outmemory()
+{
+ close();
+}
+
+
+int outmemory::classid()
+{
+ return CLASS2_OUTMEMORY;
+}
+
+
+void outmemory::doopen()
+{
+}
+
+
+void outmemory::doclose()
+{
+ clear(mem);
+}
+
+
+large outmemory::doseek(large newpos, ioseekmode mode)
+{
+ large pos;
+
+ switch (mode)
+ {
+ case IO_BEGIN:
+ pos = newpos;
+ break;
+ case IO_CURRENT:
+ pos = abspos + newpos;
+ break;
+ default: // case IO_END:
+ pos = length(mem) + newpos;
+ break;
+ }
+
+ if (limit >= 0 && pos > limit)
+ pos = limit;
+
+ return pos;
+}
+
+
+int outmemory::dorawwrite(const char* buf, int count)
+{
+ if (count <= 0)
+ return 0;
+ if (limit >= 0 && abspos + count > limit)
+ {
+ count = limit - (int)abspos;
+ if (count <= 0)
+ return 0;
+ }
+
+ // the string reallocator takes care of efficiency
+ if ((int)abspos + count > length(mem))
+ setlength(mem, (int)abspos + count);
+ memcpy(pchar(pconst(mem)) + (int)abspos, buf, count);
+ return count;
+}
+
+
+string outmemory::get_streamname()
+{
+ return "mem";
+}
+
+
+string outmemory::get_strdata()
+{
+ if (!active)
+ errstminactive();
+ return mem;
+}
+
+
+}
diff --git a/source/ptypes/poutstm.cxx b/source/ptypes/poutstm.cxx
@@ -0,0 +1,215 @@
+/*
+ *
+ * C++ Portable Types Library (PTypes)
+ * Version 2.1.1 Released 27-Jun-2007
+ *
+ * Copyright (C) 2001-2007 Hovik Melikyan
+ *
+ * http://www.melikyan.com/ptypes/
+ *
+ */
+
+#include <string.h>
+
+#ifdef WIN32
+# include <windows.h>
+#else
+# include <unistd.h>
+#endif
+
+#include "pstreams.h"
+
+
+namespace ptypes {
+
+
+outstm::outstm(bool iflusheol, int ibufsize)
+ : iobase(ibufsize), flusheol(iflusheol) {}
+
+
+outstm::~outstm()
+{
+}
+
+
+int outstm::classid()
+{
+ return CLASS_OUTSTM;
+}
+
+
+int outstm::dorawwrite(const char* buf, int count)
+{
+ if (handle == invhandle)
+ return -1;
+#ifdef WIN32
+ unsigned long ret;
+ if (!WriteFile(HANDLE(handle), buf, count, &ret, nil))
+ {
+ error(uerrno(), "Couldn't write");
+ ret = uint(-1);
+ }
+#else
+ int ret;
+ if ((ret = ::write(handle, buf, count)) < 0)
+ error(uerrno(), "Couldn't write");
+#endif
+ return ret;
+}
+
+
+int outstm::rawwrite(const char* buf, int count)
+{
+ if (!active)
+ errstminactive();
+ try
+ {
+ int ret = dorawwrite(buf, count);
+ if (ret < 0)
+ ret = 0;
+ else
+ abspos += ret;
+ chstat(IO_WRITING);
+ if (ret < count)
+ {
+ eof = true;
+ chstat(IO_EOF);
+ }
+ return ret;
+ }
+ catch (estream*)
+ {
+ eof = true;
+ chstat(IO_EOF);
+ throw;
+ }
+}
+
+
+void outstm::bufvalidate()
+{
+ if (!active)
+ errstminactive();
+ if (bufend > 0)
+ rawwrite(bufdata, bufend);
+ bufclear();
+}
+
+
+large outstm::seekx(large newpos, ioseekmode mode)
+{
+ if (bufdata != 0 && mode != IO_END)
+ {
+ large pos;
+ if (mode == IO_BEGIN)
+ pos = newpos;
+ else
+ pos = tellx() + newpos;
+ pos -= abspos;
+ if (pos >= 0 && pos <= bufpos)
+ {
+ bufpos = (int)pos;
+ eof = false;
+ return tellx();
+ }
+ }
+ return iobase::seekx(newpos, mode);
+}
+
+
+bool outstm::canwrite()
+{
+ if (bufdata != 0 && bufpos >= bufsize)
+ {
+ bufvalidate();
+ return bufend < bufsize;
+ }
+ else
+ return true;
+}
+
+
+void outstm::flush()
+{
+ if (bufdata != 0 && stmerrno == 0)
+ bufvalidate();
+}
+
+
+void outstm::put(char c)
+{
+ if (!active)
+ errstminactive();
+ if (bufdata == 0)
+ rawwrite(&c, 1);
+ else if (canwrite())
+ {
+ bufdata[bufpos] = c;
+ bufadvance(1);
+ if (c == 10 && flusheol)
+ flush();
+ }
+}
+
+
+int outstm::write(const void* buf, int count)
+{
+ if (!active)
+ errstminactive();
+ int ret = 0;
+ if (bufdata == 0)
+ ret = rawwrite(pconst(buf), count);
+ else
+ {
+ while (count > 0 && canwrite())
+ {
+ int n = imin(count, bufsize - bufpos);
+ memcpy(bufdata + bufpos, buf, n);
+ ret += n;
+ count -= n;
+ buf = pconst(buf) + n;
+ bufadvance(n);
+ }
+ }
+
+ return ret;
+}
+
+
+void outstm::put(const char* str)
+{
+ if (str != nil)
+ write(str, hstrlen(str));
+}
+
+
+void outstm::put(const string& str)
+{
+ write(pconst(str), length(str));
+}
+
+
+void outstm::putline(const char* s)
+{
+ put(s);
+ puteol();
+}
+
+
+void outstm::putline(const string& s)
+{
+ put(s);
+ puteol();
+}
+
+
+void outstm::puteol()
+{
+#ifdef WIN32
+ put(13);
+#endif
+ put(10);
+}
+
+
+}
diff --git a/source/ptypes/ppipe.cxx b/source/ptypes/ppipe.cxx
@@ -0,0 +1,51 @@
+/*
+ *
+ * C++ Portable Types Library (PTypes)
+ * Version 2.1.1 Released 27-Jun-2007
+ *
+ * Copyright (C) 2001-2007 Hovik Melikyan
+ *
+ * http://www.melikyan.com/ptypes/
+ *
+ */
+
+#ifdef WIN32
+# include <windows.h>
+#else
+# include <unistd.h>
+#endif
+
+#include "pstreams.h"
+
+
+namespace ptypes {
+
+
+void infile::pipe(outfile& out)
+{
+#ifdef WIN32
+
+ SECURITY_ATTRIBUTES sa;
+ HANDLE h[2];
+
+ sa.nLength = sizeof(SECURITY_ATTRIBUTES);
+ sa.bInheritHandle = TRUE;
+ sa.lpSecurityDescriptor = NULL;
+
+ if (!CreatePipe(&h[0], &h[1], &sa, 0))
+#else
+ int h[2];
+ if (::pipe(h) != 0)
+#endif
+ error(uerrno(), "Couldn't create a local pipe");
+
+ set_syshandle(int(h[0]));
+ peerhandle = int(h[1]);
+ out.set_syshandle(int(h[1]));
+ out.peerhandle = int(h[0]);
+ open();
+ out.open();
+}
+
+
+}
diff --git a/source/ptypes/ppodlist.cxx b/source/ptypes/ppodlist.cxx
@@ -0,0 +1,192 @@
+/*
+ *
+ * C++ Portable Types Library (PTypes)
+ * Version 2.1.1 Released 27-Jun-2007
+ *
+ * Copyright (C) 2001-2007 Hovik Melikyan
+ *
+ * http://www.melikyan.com/ptypes/
+ *
+ */
+
+#include "ptypes.h"
+
+
+namespace ptypes {
+
+
+void _podlist::idxerror()
+{
+ fatal(CRIT_FIRST + 30, "List index out of bounds");
+}
+
+
+_podlist::_podlist(int iitemsize)
+ : list(0), count(0), capacity(0), itemsize(iitemsize)
+{
+ if (itemsize <= 0 || itemsize > 255)
+ fatal(CRIT_FIRST + 37, "Invalid item size for podlist");
+}
+
+
+_podlist::~_podlist()
+{
+ set_count(0);
+}
+
+
+void _podlist::set_capacity(int newcap)
+{
+ if (newcap != capacity)
+ {
+ if (newcap < count)
+ fatal(CRIT_FIRST + 36, "List capacity can't be smaller than count");
+ list = memrealloc(list, newcap * itemsize);
+ capacity = newcap;
+ }
+}
+
+
+void _podlist::grow()
+{
+ if (capacity > count)
+ return;
+ set_capacity(capacity == 0 ? 4 : ((capacity + 1) / 2) * 3);
+}
+
+
+void _podlist::set_count(int newcount, bool zero)
+{
+ if (newcount > count)
+ {
+ if (newcount > capacity)
+ set_capacity(newcount);
+ if (zero)
+ memset(doget(count), 0, (newcount - count) * itemsize);
+ count = newcount;
+ }
+ else if (newcount < count)
+ {
+ if (newcount < 0)
+ // wrong newcount: we don't want to generate an error here.
+ // instead, we'll set count to 0 and wait until some other
+ // operation raises an error
+ newcount = 0;
+ count = newcount;
+ if (count == 0)
+ set_capacity(0);
+ }
+}
+
+
+// doXXX() methods do not perform bounds checking; used internally
+// where index is guaranteed to be valid
+
+void* _podlist::doins(int index)
+{
+ grow();
+ pchar s = pchar(doget(index));
+ if (index < count)
+ memmove(s + itemsize, s, (count - index) * itemsize);
+ count++;
+ return s;
+}
+
+
+void _podlist::doins(int index, const _podlist& t)
+{
+ if (&t == this)
+ return;
+ if (index == count)
+ add(t);
+ else
+ {
+ if (itemsize != t.itemsize)
+ fatal(CRIT_FIRST + 35, "Incompatible list");
+ if (t.count == 0)
+ return;
+ int ocnt = count;
+ set_count(ocnt + t.count);
+ pchar s = pchar(doget(index));
+ memmove(s + t.count * itemsize, s, (ocnt - index) * itemsize);
+ memcpy(s, t.list, t.count * itemsize);
+ }
+}
+
+
+void* _podlist::add()
+{
+ grow();
+ return doget(count++);
+}
+
+
+void _podlist::add(const _podlist& t)
+{
+ if (count == 0)
+ operator =(t);
+ else
+ {
+ if (itemsize != t.itemsize)
+ fatal(CRIT_FIRST + 35, "Incompatible list");
+ int ocnt = count;
+ int tcnt = t.count;
+ set_count(ocnt + tcnt);
+ memcpy(doget(ocnt), t.list, tcnt * itemsize);
+ }
+}
+
+
+_podlist& _podlist::operator =(const _podlist& t)
+{
+ if (&t != this)
+ {
+ if (itemsize != t.itemsize)
+ fatal(CRIT_FIRST + 35, "Incompatible list");
+ set_count(t.count);
+ pack();
+ memcpy(list, t.list, count * itemsize);
+ }
+ return *this;
+}
+
+
+void _podlist::dodel(int index)
+{
+ count--;
+ if (index < count)
+ {
+ pchar s = pchar(doget(index));
+ memmove(s, s + itemsize, (count - index) * itemsize);
+ }
+ else if (count == 0)
+ set_capacity(0);
+}
+
+
+void _podlist::dodel(int index, int delcount)
+{
+ if (delcount <= 0)
+ return;
+ if (index + delcount > count)
+ delcount = count - index;
+ count -= delcount;
+ if (index < count)
+ {
+ pchar s = pchar(doget(index));
+ memmove(s, s + delcount * itemsize, (count - index) * itemsize);
+ }
+ else if (count == 0)
+ set_capacity(0);
+}
+
+
+void _podlist::dopop()
+{
+ if (--count == 0)
+ set_capacity(0);
+}
+
+
+}
+
diff --git a/source/ptypes/pport.h b/source/ptypes/pport.h
@@ -0,0 +1,189 @@
+/*
+ *
+ * C++ Portable Types Library (PTypes)
+ * Version 2.1.1 Released 27-Jun-2007
+ *
+ * Copyright (C) 2001-2007 Hovik Melikyan
+ *
+ * http://www.melikyan.com/ptypes/
+ *
+ */
+
+#ifndef __PPORT_H__
+#define __PPORT_H__
+
+
+#if defined(linux)
+# include <stdint.h> // for uintptr_t
+#endif
+
+#include <sys/types.h>
+
+
+#ifndef __cplusplus
+# error "This is a C++ source"
+#endif
+
+//
+// Windows DLL export/import and calling convention macros
+//
+
+#ifdef WIN32
+# if defined(PTYPES_DLL_EXPORTS)
+# define ptpublic __declspec(dllexport)
+# elif defined(PTYPES_DLL)
+# define ptpublic __declspec(dllimport)
+# else
+# define ptpublic
+# endif
+# define ptdecl __stdcall
+# define __PFASTCALL __fastcall
+#else
+# define ptpublic
+# define ptdecl
+# define __PFASTCALL
+#endif
+
+
+//
+// versioning
+//
+
+
+extern "C" ptpublic unsigned long __ptypes_version;
+
+// this enables old algebraic list interfaces; NO_PTYPES19_COMPAT
+// can be defined at command line
+#if !defined(NO_PTYPES19_COMPAT)
+# define PTYPES19_COMPAT
+#endif
+
+
+namespace ptypes {
+
+
+#ifdef _MSC_VER
+// we don't want "unreferenced inline function" warning
+# pragma warning (disable: 4514)
+// ... also "copy constructor/assignment operator could not be generated"
+# pragma warning (disable: 4511)
+# pragma warning (disable: 4512)
+// disable deprecation warnings for snprintf and others
+# pragma warning (disable: 4996)
+#endif
+
+#if defined(_DEBUG) && !defined(DEBUG)
+# define DEBUG
+#endif
+
+#if defined(__WIN32__) && !defined(WIN32)
+# define WIN32
+#endif
+
+// __APPLE__ is the only predefined macro on MacOS X
+#if defined(__APPLE__)
+# define __DARWIN__
+#endif
+
+// CHECK_BOUNDS enables bounds checking for strings and lists
+#if defined(DEBUG) && !defined(CHECK_BOUNDS)
+# define CHECK_BOUNDS
+#endif
+
+// COUNT_OBJALLOC helps to keep track of the number of
+// objects created/destroyed
+#if defined(DEBUG) && !defined(COUNT_OBJALLOC)
+# define COUNT_OBJALLOC
+#endif
+
+
+//
+// useful typedefs
+//
+
+typedef unsigned int uint;
+typedef unsigned long ulong;
+typedef unsigned short ushort;
+typedef unsigned char uchar;
+typedef signed char schar;
+typedef char* pchar;
+typedef const char* pconst;
+typedef void* ptr;
+typedef int* pint;
+
+#ifdef WIN32
+ typedef size_t pintptr;
+#else
+ typedef uintptr_t pintptr;
+#endif
+
+
+//
+// portable 64-bit integers
+//
+
+#if defined(_MSC_VER) || defined(__BORLANDC__)
+ typedef __int64 large;
+ typedef unsigned __int64 ularge;
+# define LLCONST(a) (a##i64)
+#else
+ typedef long long large;
+ typedef unsigned long long ularge;
+# define LLCONST(a) (a##ll)
+#endif
+
+#define LARGE_MIN (LLCONST(-9223372036854775807)-1)
+#define LARGE_MAX (LLCONST(9223372036854775807))
+#define ULARGE_MAX (LLCONST(18446744073709551615u))
+
+#if defined(_MSC_VER) || defined(__BORLANDC__)
+# define strcasecmp stricmp
+#endif
+#if (defined(_MSC_VER) && _MSC_VER < 1900) || defined(__BORLANDC__)
+# define snprintf _snprintf
+#endif
+
+
+//
+// misc.
+//
+
+// I like Pascal's nil
+#define nil 0
+
+inline int imax(int x, int y) { return (x > y) ? x : y; }
+inline int imin(int x, int y) { return (x < y) ? x : y; }
+inline large lmax(large x, large y) { return (x > y) ? x : y; }
+inline large lmin(large x, large y) { return (x < y) ? x : y; }
+
+
+//
+// critical error processing
+//
+
+#define CRIT_FIRST 0xC0000
+
+typedef void (ptdecl *_pcrithandler)(int code, const char* msg);
+
+ptpublic _pcrithandler ptdecl getcrithandler();
+ptpublic _pcrithandler ptdecl setcrithandler(_pcrithandler newh);
+
+ptpublic void ptdecl fatal(int code, const char* msg);
+
+
+//
+// memory management (undocumented)
+// hides some BSD* incompatibility issues
+//
+
+ptpublic void* ptdecl memalloc(uint a);
+ptpublic void* ptdecl memrealloc(void* p, uint a);
+ptpublic void ptdecl memfree(void* p);
+ptpublic void ptdecl memerror();
+ptpublic int ptdecl memquantize(int);
+
+
+}
+
+
+#endif // __PPORT_H__
diff --git a/source/ptypes/pputf.cxx b/source/ptypes/pputf.cxx
@@ -0,0 +1,287 @@
+/*
+ *
+ * C++ Portable Types Library (PTypes)
+ * Version 2.1.1 Released 27-Jun-2007
+ *
+ * Copyright (C) 2001-2007 Hovik Melikyan
+ *
+ * http://www.melikyan.com/ptypes/
+ *
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+
+#include "ptypes.h"
+#include "pstreams.h"
+#include "pinet.h" // for ipaddress type
+#include "ptime.h" // for dttotm()
+
+#ifndef PTYPES_ST
+#include "pasync.h" // for mutex locking in logfile::vputf()
+#endif
+
+
+namespace ptypes {
+
+
+// %t and %T formats
+const char* const shorttimefmt = "%d-%b-%Y %X";
+const char* const longtimefmt = "%a %b %d %X %Y";
+
+
+static cset fmtopts = " #+~-0-9.";
+
+
+enum fmt_type_t
+{
+ FMT_NONE,
+ FMT_CHAR,
+ FMT_SHORT,
+ FMT_INT,
+ FMT_LONG,
+ FMT_LARGE,
+ FMT_STR,
+ FMT_PTR,
+ FMT_DOUBLE,
+ FMT_LONG_DOUBLE,
+ FMT_IPADDR,
+ FMT_TIME,
+ FMT_LONGTIME
+};
+
+
+void outstm::vputf(const char* fmt, va_list va)
+{
+ const char* p = fmt;
+ while (*p != 0)
+ {
+ // write out raw data between format specifiers
+ const char* e = strchr(p, '%');
+ if (e == 0)
+ e = p + strlen(p);
+ if (e > p)
+ write(p, e - p);
+
+ if (*e != '%')
+ break;
+
+ e++;
+ if (*e == '%')
+ {
+ // write out a single '%'
+ put('%');
+ p = e + 1;
+ continue;
+ }
+
+ // build a temporary buffer for the conversion specification
+ char fbuf[128];
+ fbuf[0] = '%';
+ char* f = fbuf + 1;
+ bool modif = false;
+
+ // formatting flags and width specifiers
+ while (*e & fmtopts && uint(f - fbuf) < sizeof(fbuf) - 5)
+ {
+ *f++ = *e++;
+ modif = true;
+ }
+
+ // prefixes
+ fmt_type_t fmt_type = FMT_NONE;
+ switch(*e)
+ {
+ case 'h':
+ fmt_type = FMT_SHORT;
+ *f++ = *e++;
+ break;
+ case 'L':
+ fmt_type = FMT_LONG_DOUBLE;
+ *f++ = *e++;
+ break;
+ case 'l':
+ e++;
+ if (*e == 'l')
+ {
+#if defined(_MSC_VER) || defined(__BORLANDC__)
+ *f++ = 'I';
+ *f++ = '6';
+ *f++ = '4';
+#else
+ *f++ = 'l';
+ *f++ = 'l';
+#endif
+ e++;
+ fmt_type = FMT_LARGE;
+ }
+ else
+ {
+ *f++ = 'l';
+ fmt_type = FMT_LONG;
+ }
+ break;
+ }
+
+ // format specifier
+ switch(*e)
+ {
+ case 'c':
+ fmt_type = FMT_CHAR;
+ *f++ = *e++;
+ break;
+ case 'd':
+ case 'i':
+ case 'o':
+ case 'u':
+ case 'x':
+ case 'X':
+ if (fmt_type < FMT_SHORT || fmt_type > FMT_LARGE)
+ fmt_type = FMT_INT;
+ *f++ = *e++;
+ break;
+ case 'e':
+ case 'E':
+ case 'f':
+ case 'g':
+ case 'G':
+ if (fmt_type != FMT_LONG_DOUBLE)
+ fmt_type = FMT_DOUBLE;
+ *f++ = *e++;
+ break;
+ case 's':
+ fmt_type = FMT_STR;
+ *f++ = *e++;
+ break;
+ case 'p':
+ fmt_type = FMT_PTR;
+ *f++ = *e++;
+ break;
+ case 'a':
+ fmt_type = FMT_IPADDR;
+ *f++ = *e++;
+ break;
+ case 't':
+ fmt_type = FMT_TIME;
+ *f++ = *e++;
+ break;
+ case 'T':
+ fmt_type = FMT_LONGTIME;
+ *f++ = *e++;
+ break;
+ }
+
+ if (fmt_type == FMT_NONE)
+ break;
+
+ *f = 0;
+
+ // some formatters are processed here 'manually',
+ // while others are passed to snprintf
+ char buf[4096];
+ int s = 0;
+ switch(fmt_type)
+ {
+ case FMT_NONE:
+ break; // to avoid compiler warning
+ case FMT_CHAR:
+ if (modif)
+ s = snprintf(buf, sizeof(buf), fbuf, va_arg(va,int));
+ else
+ put(char(va_arg(va,int)));
+ break;
+ case FMT_SHORT:
+ s = snprintf(buf, sizeof(buf), fbuf, va_arg(va,int));
+ break;
+ case FMT_INT:
+ s = snprintf(buf, sizeof(buf), fbuf, va_arg(va,int));
+ break;
+ case FMT_LONG:
+ s = snprintf(buf, sizeof(buf), fbuf, va_arg(va,long));
+ break;
+ case FMT_LARGE:
+ s = snprintf(buf, sizeof(buf), fbuf, va_arg(va,large));
+ break;
+ case FMT_STR:
+ if (modif)
+ s = snprintf(buf, sizeof(buf), fbuf, va_arg(va,char*));
+ else
+ put(va_arg(va,const char*));
+ break;
+ case FMT_PTR:
+ s = snprintf(buf, sizeof(buf), fbuf, va_arg(va,void*));
+ break;
+ case FMT_DOUBLE:
+ s = snprintf(buf, sizeof(buf), fbuf, va_arg(va,double));
+ break;
+ case FMT_LONG_DOUBLE:
+ s = snprintf(buf, sizeof(buf), fbuf, va_arg(va,long double));
+ break;
+
+ case FMT_IPADDR:
+ {
+ ipaddress ip = va_arg(va,long);
+ s = snprintf(buf, sizeof(buf), "%d.%d.%d.%d",
+ uint(ip[0]), uint(ip[1]), uint(ip[2]), uint(ip[3]));
+ }
+ break;
+
+ case FMT_TIME:
+ case FMT_LONGTIME:
+ {
+ const char* const fmt = (fmt_type == FMT_TIME) ? shorttimefmt : longtimefmt;
+ struct tm t;
+ datetime dt = va_arg(va,large);
+ if (dt < 0)
+ dt = 0;
+ s = strftime(buf, sizeof(buf), fmt, dttotm(dt, t));
+ }
+ break;
+ }
+ if (s > 0)
+ write(buf, s);
+
+ p = e;
+ }
+}
+
+
+void outstm::putf(const char* fmt, ...)
+{
+ va_list va;
+ va_start(va, fmt);
+ vputf(fmt, va);
+ va_end(va);
+}
+
+
+void fdxstm::putf(const char* fmt, ...)
+{
+ va_list va;
+ va_start(va, fmt);
+ out.vputf(fmt, va);
+ va_end(va);
+}
+
+
+void logfile::vputf(const char* fmt, va_list va)
+{
+#ifndef PTYPES_ST
+ scopelock sl(lock);
+#endif
+ outfile::vputf(fmt, va);
+}
+
+
+void logfile::putf(const char* fmt, ...)
+{
+ va_list va;
+ va_start(va, fmt);
+ vputf(fmt, va);
+ va_end(va);
+}
+
+
+}
diff --git a/source/ptypes/prwlock.cxx b/source/ptypes/prwlock.cxx
@@ -0,0 +1,193 @@
+/*
+ *
+ * C++ Portable Types Library (PTypes)
+ * Version 2.1.1 Released 27-Jun-2007
+ *
+ * Copyright (C) 2001-2007 Hovik Melikyan
+ *
+ * http://www.melikyan.com/ptypes/
+ *
+ */
+
+#ifdef WIN32
+# include <windows.h>
+#else
+# include <pthread.h>
+#endif
+
+#include "pasync.h"
+
+
+namespace ptypes {
+
+
+static void rwlock_fail()
+{
+ fatal(CRIT_FIRST + 41, "rwlock failed");
+}
+
+
+inline void rwlock_syscheck(int r)
+{
+ if (r != 0)
+ rwlock_fail();
+}
+
+
+#ifdef __PTYPES_RWLOCK__
+
+
+# ifdef WIN32
+
+//
+// this implementation of the read/write lock is derived
+// from Apache Portable Runtime (APR) source, which
+// in turn was originally based on an erroneous (or just
+// incomplete?) example in one of the MSDN technical articles.
+//
+
+rwlock::rwlock()
+ : mutex(), readcnt(-1), writecnt(0)
+{
+ reading = CreateEvent(0, true, false, 0);
+ finished = CreateEvent(0, false, true, 0);
+ if (reading == 0 || finished == 0)
+ rwlock_fail();
+}
+
+
+rwlock::~rwlock()
+{
+ CloseHandle(reading);
+ CloseHandle(finished);
+}
+
+
+void rwlock::rdlock()
+{
+ if (pincrement(&readcnt) == 0)
+ {
+ WaitForSingleObject(finished, INFINITE);
+ SetEvent(reading);
+ }
+ WaitForSingleObject(reading, INFINITE);
+}
+
+
+void rwlock::wrlock()
+{
+ mutex::enter();
+ WaitForSingleObject(finished, INFINITE);
+ writecnt++;
+}
+
+
+void rwlock::unlock()
+{
+ if (writecnt != 0)
+ {
+ writecnt--;
+ SetEvent(finished);
+ mutex::leave();
+ }
+ else if (pdecrement(&readcnt) < 0)
+ {
+ ResetEvent(reading);
+ SetEvent(finished);
+ }
+}
+
+
+# else // !defined(WIN32)
+
+//
+// for other platforms that lack POSIX rwlock we implement
+// the rwlock object using POSIX condvar. the code below
+// is based on Sean Burke's algorithm posted in
+// comp.programming.threads.
+//
+
+
+rwlock::rwlock()
+ : locks(0), writers(0), readers(0)
+{
+ rwlock_syscheck(pthread_mutex_init(&mtx, 0));
+ rwlock_syscheck(pthread_cond_init(&readcond, 0));
+ rwlock_syscheck(pthread_cond_init(&writecond, 0));
+}
+
+
+rwlock::~rwlock()
+{
+ pthread_cond_destroy(&writecond);
+ pthread_cond_destroy(&readcond);
+ pthread_mutex_destroy(&mtx);
+}
+
+
+void rwlock::rdlock()
+{
+ pthread_mutex_lock(&mtx);
+ readers++;
+ while (locks < 0)
+ pthread_cond_wait(&readcond, &mtx);
+ readers--;
+ locks++;
+ pthread_mutex_unlock(&mtx);
+}
+
+
+void rwlock::wrlock()
+{
+ pthread_mutex_lock(&mtx);
+ writers++;
+ while (locks != 0)
+ pthread_cond_wait(&writecond, &mtx);
+ locks = -1;
+ writers--;
+ pthread_mutex_unlock(&mtx);
+}
+
+
+void rwlock::unlock()
+{
+ pthread_mutex_lock(&mtx);
+ if (locks > 0)
+ {
+ locks--;
+ if (locks == 0)
+ pthread_cond_signal(&writecond);
+ }
+ else
+ {
+ locks = 0;
+ if (readers != 0)
+ pthread_cond_broadcast(&readcond);
+ else
+ pthread_cond_signal(&writecond);
+ }
+ pthread_mutex_unlock(&mtx);
+}
+
+
+# endif // !defined(WIN32)
+
+
+#else // !defined(__PTYPES_RWLOCK__)
+
+//
+// for other systems we declare a fully-inlined rwlock
+// object in pasync.h
+//
+
+rwlock::rwlock()
+{
+ rwlock_syscheck(pthread_rwlock_init(&rw, 0));
+}
+
+
+#endif
+
+
+
+}
diff --git a/source/ptypes/psemaphore.cxx b/source/ptypes/psemaphore.cxx
@@ -0,0 +1,76 @@
+/*
+ *
+ * C++ Portable Types Library (PTypes)
+ * Version 2.1.1 Released 27-Jun-2007
+ *
+ * Copyright (C) 2001-2007 Hovik Melikyan
+ *
+ * http://www.melikyan.com/ptypes/
+ *
+ */
+
+#include <errno.h>
+
+#ifdef WIN32
+# include <windows.h>
+#else
+# include <pthread.h>
+#endif
+
+#include "pasync.h"
+
+
+namespace ptypes {
+
+
+#ifndef __SEM_TO_TIMEDSEM__
+
+
+static void sem_fail()
+{
+ fatal(CRIT_FIRST + 41, "Semaphore failed");
+}
+
+
+semaphore::semaphore(int initvalue)
+{
+ if (sem_init(&handle, 0, initvalue) != 0)
+ sem_fail();
+}
+
+
+semaphore::~semaphore()
+{
+ sem_destroy(&handle);
+}
+
+
+void semaphore::wait()
+{
+ int err;
+ do {
+ err = sem_wait(&handle);
+ } while (err == -1 && errno == EINTR);
+ if (err != 0)
+ sem_fail();
+}
+
+
+void semaphore::post()
+{
+ if (sem_post(&handle) != 0)
+ sem_fail();
+}
+
+
+#else
+
+
+int _psemaphore_dummy_symbol; // avoid ranlib's warning message
+
+
+#endif
+
+
+
+}
diff --git a/source/ptypes/pstdio.cxx b/source/ptypes/pstdio.cxx
@@ -0,0 +1,155 @@
+/*
+ *
+ * C++ Portable Types Library (PTypes)
+ * Version 2.1.1 Released 27-Jun-2007
+ *
+ * Copyright (C) 2001-2007 Hovik Melikyan
+ *
+ * http://www.melikyan.com/ptypes/
+ *
+ */
+
+#include "pport.h"
+#include "pstreams.h"
+
+#ifdef WIN32
+# include <windows.h>
+#else
+# include <unistd.h>
+#endif
+
+
+namespace ptypes {
+
+infile pin;
+logfile pout;
+logfile perr;
+outnull pnull;
+
+
+static class _stdio_init
+{
+public:
+ _stdio_init();
+} _stdio_init_inst;
+
+
+#ifdef WIN32
+
+static HANDLE DuplicateSysHandle(DWORD stdh)
+{
+ HANDLE hold = GetStdHandle(stdh);
+ HANDLE hnew = 0;
+ DuplicateHandle(GetCurrentProcess(), hold, GetCurrentProcess(),
+ &hnew, 0, true, DUPLICATE_SAME_ACCESS);
+ return hnew;
+}
+
+#endif
+
+
+_stdio_init::_stdio_init()
+{
+#ifdef WIN32
+ pin.set_syshandle(int(DuplicateSysHandle(STD_INPUT_HANDLE)));
+ pout.set_syshandle(int(DuplicateSysHandle(STD_OUTPUT_HANDLE)));
+ perr.set_syshandle(int(DuplicateSysHandle(STD_ERROR_HANDLE)));
+#else
+ pin.set_syshandle(::dup(STDIN_FILENO));
+ pout.set_syshandle(::dup(STDOUT_FILENO));
+ perr.set_syshandle(::dup(STDERR_FILENO));
+#endif
+
+ pin.set_bufsize(4096);
+ pin.open();
+ pout.open();
+ perr.open();
+
+ pnull.open();
+
+ // prevent others from freeing these objects, if assigned to a variant.
+ // will need to handle reference counting for static objects better. any ideas?
+ addref(&pin);
+ addref(&pout);
+ addref(&perr);
+ addref(&pnull);
+
+ // this is to show objalloc = 0 at program exit
+ objalloc -= 4;
+}
+
+
+//
+// null output stream
+//
+
+
+outnull::outnull()
+ : outstm(0)
+{
+}
+
+
+outnull::~outnull()
+{
+ close();
+}
+
+
+int outnull::dorawwrite(const char*, int)
+{
+ return 0;
+}
+
+
+void outnull::doopen()
+{
+}
+
+
+void outnull::doclose()
+{
+}
+
+
+string outnull::get_streamname()
+{
+ return "<null>";
+}
+
+
+//
+// logfile - file output with thread-safe putf()
+//
+
+logfile::logfile(): outfile()
+{
+ set_bufsize(0);
+}
+
+
+logfile::logfile(const char* ifn, bool iappend): outfile(ifn, iappend)
+{
+ set_bufsize(0);
+}
+
+
+logfile::logfile(const string& ifn, bool iappend): outfile(ifn, iappend)
+{
+ set_bufsize(0);
+}
+
+
+logfile::~logfile()
+{
+}
+
+
+int logfile::classid()
+{
+ return CLASS3_LOGFILE;
+}
+
+
+}
+
diff --git a/source/ptypes/pstrcase.cxx b/source/ptypes/pstrcase.cxx
@@ -0,0 +1,73 @@
+/*
+ *
+ * C++ Portable Types Library (PTypes)
+ * Version 2.1.1 Released 27-Jun-2007
+ *
+ * Copyright (C) 2001-2007 Hovik Melikyan
+ *
+ * http://www.melikyan.com/ptypes/
+ *
+ */
+
+#include "ptypes.h"
+
+
+namespace ptypes {
+
+
+string ptdecl lowercase(const char* p)
+{
+ // we rely on the function locase() which converts one single
+ // character to lower case. all locale specific things can be
+ // settled down in the future releases.
+ string r;
+ if (p != nil)
+ {
+ char* d = setlength(r, strlen(p));
+ while (*p != 0)
+ *d++ = locase(*p++);
+ }
+ return r;
+}
+
+
+string ptdecl lowercase(const string& s)
+{
+ // this function does practically nothing if the string s
+ // contains no uppercase characters. once an uppercase character
+ // is encountered, the string is copied to another buffer and the
+ // rest is done as usual.
+
+ string r = s;
+
+ // a trick to get a non-const pointer without making
+ // the string unique
+ char* p = pchar(pconst(r));
+ bool u = false;
+ int i = 0;
+
+ while (*p != 0)
+ {
+ char c = locase(*p);
+ // if the character went lowercase...
+ if (c != *p)
+ {
+ // if the resulting string r is not unique yet...
+ if (!u)
+ {
+ // ... make it unique and adjust the pointer p accordingly
+ // this is done only once.
+ p = unique(r) + i;
+ u = true;
+ }
+ *p = c;
+ }
+ p++;
+ i++;
+ }
+
+ return r;
+}
+
+
+}
diff --git a/source/ptypes/pstrconv.cxx b/source/ptypes/pstrconv.cxx
@@ -0,0 +1,147 @@
+/*
+ *
+ * C++ Portable Types Library (PTypes)
+ * Version 2.1.1 Released 27-Jun-2007
+ *
+ * Copyright (C) 2001-2007 Hovik Melikyan
+ *
+ * http://www.melikyan.com/ptypes/
+ *
+ */
+
+#include <string.h>
+
+#include "ptypes.h"
+
+
+namespace ptypes {
+
+
+static const char* _itobase(large value, char* buf, int base, int& len, bool _signed)
+{
+ // internal conversion routine: converts the value to a string
+ // at the end of the buffer and returns a pointer to the first
+ // character. this is to get rid of copying the string to the
+ // beginning of the buffer, since finally the string is supposed
+ // to be copied to a dynamic string in itostring(). the buffer
+ // must be at least 65 bytes long.
+
+ static char digits[65] =
+ "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
+
+ char* pdigits;
+ if (base > 36)
+ pdigits = digits; // start from '.'
+ else
+ pdigits = digits + 2; // start from '0'
+
+ int i = 64;
+ buf[i] = 0;
+
+ bool neg = false;
+ ularge v = value;
+ if (_signed && base == 10 && value < 0)
+ {
+ v = -value;
+ // since we can't handle the lowest signed 64-bit value, we just
+ // return a built-in string.
+ if (large(v) < 0) // the LLONG_MIN negated results in the same value
+ {
+ len = 20;
+ return "-9223372036854775808";
+ }
+ neg = true;
+ }
+
+ do
+ {
+ buf[--i] = pdigits[uint(v % base)];
+ v /= base;
+ } while (v > 0);
+
+ if (neg)
+ buf[--i] = '-';
+
+ len = 64 - i;
+ return buf + i;
+}
+
+
+static void _itobase2(string& result, large value, int base, int width, char padchar, bool _signed)
+{
+ if (base < 2 || base > 64)
+ {
+ clear(result);
+ return;
+ }
+
+ char buf[65]; // the longest possible string is when base=2
+ int reslen;
+ const char* p = _itobase(value, buf, base, reslen, _signed);
+
+ if (width > reslen)
+ {
+ if (padchar == 0)
+ {
+ // default pad char
+ if (base == 10)
+ padchar = ' ';
+ else if (base > 36)
+ padchar = '.';
+ else
+ padchar = '0';
+ }
+
+ setlength(result, width);
+ bool neg = *p == '-';
+ width -= reslen;
+ memset(pchar(pconst(result)) + neg, padchar, width);
+ memcpy(pchar(pconst(result)) + width + neg, p + neg, reslen - neg);
+ if (neg)
+ *pchar(pconst(result)) = '-';
+ }
+ else
+ assign(result, p, reslen);
+}
+
+
+string ptdecl itostring(large value, int base, int width, char padchar)
+{
+ string result;
+ _itobase2(result, value, base, width, padchar, true);
+ return result;
+}
+
+
+string ptdecl itostring(ularge value, int base, int width, char padchar)
+{
+ string result;
+ _itobase2(result, value, base, width, padchar, false);
+ return result;
+}
+
+
+string ptdecl itostring(int value, int base, int width, char padchar)
+{
+ string result;
+ _itobase2(result, large(value), base, width, padchar, true);
+ return result;
+}
+
+
+string ptdecl itostring(uint value, int base, int width, char padchar)
+{
+ string result;
+ _itobase2(result, ularge(value), base, width, padchar, false);
+ return result;
+}
+
+
+string ptdecl itostring(large v) { return itostring(v, 10, 0, ' '); }
+string ptdecl itostring(ularge v) { return itostring(v, 10, 0, ' '); }
+string ptdecl itostring(int v) { return itostring(large(v), 10, 0, ' '); }
+string ptdecl itostring(uint v) { return itostring(ularge(v), 10, 0, ' '); }
+
+
+}
+
diff --git a/source/ptypes/pstreams.h b/source/ptypes/pstreams.h
@@ -0,0 +1,807 @@
+/*
+ *
+ * C++ Portable Types Library (PTypes)
+ * Version 2.1.1 Released 27-Jun-2007
+ *
+ * Copyright (C) 2001-2007 Hovik Melikyan
+ *
+ * http://www.melikyan.com/ptypes/
+ *
+ */
+
+#ifndef __PSTREAMS_H__
+#define __PSTREAMS_H__
+
+#ifndef __PPORT_H__
+#include "pport.h"
+#endif
+
+#ifndef __PTYPES_H__
+#include "ptypes.h"
+#endif
+
+#ifndef PTYPES_ST
+# ifndef __PASYNC_H__
+# include "pasync.h" // for logfile.lock
+# endif
+#endif
+
+#include <stdarg.h>
+#include <errno.h>
+
+
+#ifdef WIN32
+# define _WINSOCKAPI_ // prevent inclusion of winsock.h, because we need winsock2.h
+# include "windows.h" // for OVERLAPPED
+#endif
+
+
+namespace ptypes {
+
+
+#ifdef _MSC_VER
+#pragma pack(push, 4)
+#endif
+
+
+// -------------------------------------------------------------------- //
+// --- abstract stream i/o classes ----------------------------------- //
+// -------------------------------------------------------------------- //
+
+
+//
+// stream exception class
+//
+
+class iobase;
+
+class ptpublic estream: public exception
+{
+protected:
+ int code;
+ iobase* errstm;
+public:
+ estream(iobase* ierrstm, int icode, const char* imsg);
+ estream(iobase* ierrstm, int icode, const string& imsg);
+ virtual ~estream();
+ int get_code() { return code; }
+ iobase* get_errstm() { return errstm; }
+};
+
+
+typedef void (ptdecl *iostatusevent)(iobase* sender, int code);
+
+ptpublic int ptdecl unixerrno();
+ptpublic const char* ptdecl unixerrmsg(int code);
+
+
+// status codes: compatible with WinInet API
+// additional status codes are defined in pinet.h for ipsocket
+
+const int IO_CREATED = 1;
+const int IO_OPENING = 5;
+const int IO_OPENED = 35;
+const int IO_READING = 37;
+const int IO_WRITING = 38;
+const int IO_EOF = 45;
+const int IO_CLOSING = 250;
+const int IO_CLOSED = 253;
+
+
+//
+// iobase
+//
+
+enum ioseekmode
+{
+ IO_BEGIN,
+ IO_CURRENT,
+ IO_END
+};
+
+
+const int invhandle = -1;
+
+
+class ptpublic iobase: public component
+{
+ friend class fdxoutstm;
+
+protected:
+ bool active; // active status, changed by open() and close()
+ bool cancelled; // the stream was cancelled by cancel()
+ bool eof; // end of file reached, only for input streams
+ int handle; // used in many derivative classes
+ large abspos; // physical stream position
+ int bufsize; // buffer size, can be changed only when not active
+ char* bufdata; // internal: allocated buffer data
+ int bufpos; // internal: current position
+ int bufend; // internal: current data size in the buffer
+ int stmerrno; // UNIX-compatible error numbers, see comments in piobase.cxx
+ string deferrormsg; // internal: default error message when an exception is thrown,
+ int status; // stream status code, see IO_xxx constants above
+ iostatusevent onstatus; // user-defined status change handler
+
+ virtual void bufalloc();
+ virtual void buffree();
+ void bufclear() { bufpos = 0; bufend = 0; }
+
+ void errstminactive();
+ void errbufrequired();
+ void requireactive() { if (!active) errstminactive(); }
+ void requirebuf() { requireactive(); if (bufdata == 0) errbufrequired(); }
+ int convertoffset(large);
+
+ virtual void doopen() = 0;
+ virtual void doclose();
+ virtual large doseek(large newpos, ioseekmode mode);
+
+ virtual void chstat(int newstat);
+ virtual int uerrno();
+ virtual const char* uerrmsg(int code);
+
+public:
+ iobase(int ibufsize = -1);
+ virtual ~iobase();
+
+ void open();
+ void close();
+ void cancel();
+ void reopen() { open(); }
+ large seekx(large newpos, ioseekmode mode = IO_BEGIN);
+ int seek(int newpos, ioseekmode mode = IO_BEGIN) { return convertoffset(seekx(newpos, mode)); }
+ void error(int code, const char* defmsg);
+ virtual void flush();
+
+ virtual string get_errormsg();
+ virtual string get_errstmname();
+ virtual string get_streamname() = 0;
+
+ bool get_active() { return active; }
+ void set_active(bool newval);
+ bool get_cancelled() { return cancelled; }
+ void set_cancelled(bool newval) { cancelled = newval; }
+ int get_handle() { return handle; }
+ int get_bufsize() { return bufsize; }
+ void set_bufsize(int newval);
+ int get_stmerrno() { return stmerrno; }
+ int get_status() { return status; }
+ iostatusevent get_onstatus() { return onstatus; }
+ void set_onstatus(iostatusevent newval) { onstatus = newval; }
+};
+typedef iobase* piobase;
+
+
+ptpublic extern int defbufsize;
+ptpublic extern int stmbalance;
+
+
+//
+// instm - abstract input stream
+//
+
+const char eofchar = 0;
+
+class ptpublic instm: public iobase
+{
+protected:
+ virtual int dorawread(char* buf, int count);
+ int rawread(char* buf, int count);
+ virtual void bufvalidate();
+ void skipeol();
+
+public:
+ instm(int ibufsize = -1);
+ virtual ~instm();
+ virtual int classid();
+
+ bool get_eof();
+ void set_eof(bool ieof) { eof = ieof; }
+ bool get_eol();
+ int get_dataavail();
+ char preview();
+ char get();
+ void putback();
+ string token(const cset& chars);
+ string token(const cset& chars, int limit);
+ int token(const cset& chars, char* buf, int size);
+ string line();
+ string line(int limit);
+ int line(char* buf, int size, bool eateol = true);
+ int read(void* buf, int count);
+ int skip(int count);
+ int skiptoken(const cset& chars);
+ void skipline(bool eateol = true);
+ large tellx();
+ int tell() { return convertoffset(tellx()); }
+ large seekx(large newpos, ioseekmode mode = IO_BEGIN);
+ int seek(int newpos, ioseekmode mode = IO_BEGIN) { return convertoffset(seekx(newpos, mode)); }
+};
+typedef instm* pinstm;
+
+
+//
+// outstm - abstract output stream
+//
+
+class ptpublic outstm: public iobase
+{
+protected:
+ bool flusheol;
+
+ virtual int dorawwrite(const char* buf, int count);
+ int rawwrite(const char* buf, int count);
+ virtual void bufvalidate();
+ void bufadvance(int delta)
+ { bufpos += delta; if (bufend < bufpos) bufend = bufpos; }
+ bool canwrite();
+
+public:
+ outstm(bool iflusheol = false, int ibufsize = -1);
+ virtual ~outstm();
+ virtual int classid();
+
+ bool get_flusheol() { return flusheol; }
+ void set_flusheol(bool newval) { flusheol = newval; }
+
+ virtual void flush();
+ bool get_eof() { return eof; }
+ void put(char c);
+ void put(const char* str);
+ void put(const string& str);
+ void vputf(const char* fmt, va_list);
+ void putf(const char* fmt, ...);
+ void putline(const char* str);
+ void putline(const string& str);
+ void puteol();
+ int write(const void* buf, int count);
+ large tellx() { return abspos + bufpos; }
+ int tell() { return convertoffset(tellx()); }
+ large seekx(large newpos, ioseekmode mode = IO_BEGIN);
+ int seek(int newpos, ioseekmode mode = IO_BEGIN) { return convertoffset(seekx(newpos, mode)); }
+};
+typedef outstm* poutstm;
+
+
+// %t and %T formats
+ptpublic extern const char* const shorttimefmt; // "%d-%b-%Y %X"
+ptpublic extern const char* const longtimefmt; // "%a %b %d %X %Y"
+
+
+//
+// internal class used in fdxstm
+//
+
+class ptpublic fdxstm;
+
+
+class ptpublic fdxoutstm: public outstm
+{
+ friend class fdxstm;
+
+protected:
+ fdxstm* in;
+ virtual void chstat(int newstat);
+ virtual int uerrno();
+ virtual const char* uerrmsg(int code);
+ virtual void doopen();
+ virtual void doclose();
+ virtual int dorawwrite(const char* buf, int count);
+
+public:
+ fdxoutstm(int ibufsize, fdxstm* iin);
+ virtual ~fdxoutstm();
+ virtual string get_streamname();
+};
+typedef fdxstm* pfdxstm;
+
+
+//
+// fdxstm: abstract full-duplex stream (for sockets and pipes)
+//
+
+class ptpublic fdxstm: public instm
+{
+ friend class fdxoutstm;
+
+protected:
+ fdxoutstm out;
+
+ virtual int dorawwrite(const char* buf, int count);
+
+public:
+
+ fdxstm(int ibufsize = -1);
+ virtual ~fdxstm();
+ virtual int classid();
+
+ void set_bufsize(int newval); // sets both input and output buffer sizes
+
+ void open(); // rewritten to pass the call to the output stream too
+ void close();
+ void cancel();
+ virtual void flush();
+ large tellx(bool); // true for input and false for output
+ int tell(bool forin) { return convertoffset(tellx(forin)); }
+
+ // output interface: pretend this class is derived both
+ // from instm and outstm. actually we can't use multiple
+ // inheritance here, since this is a full-duplex stream,
+ // hence everything must be duplicated for input and output
+ void putf(const char* fmt, ...);
+ void put(char c) { out.put(c); }
+ void put(const char* str) { out.put(str); }
+ void put(const string& str) { out.put(str); }
+ void putline(const char* str) { out.putline(str); }
+ void putline(const string& str) { out.putline(str); }
+ void puteol() { out.puteol(); }
+ int write(const void* buf, int count) { return out.write(buf, count); }
+ bool get_flusheol() { return out.get_flusheol(); }
+ void set_flusheol(bool newval) { out.set_flusheol(newval); }
+
+ operator outstm&() { return out; }
+};
+
+
+//
+// abstract input filter class
+//
+
+class ptpublic infilter: public instm
+{
+protected:
+ instm* stm;
+ char* savebuf;
+ int savecount;
+ string postponed;
+
+ void copytobuf(string& s);
+ void copytobuf(pconst& buf, int& count);
+ bool copytobuf(char c);
+
+ virtual void freenotify(component* sender);
+ virtual void doopen();
+ virtual void doclose();
+ virtual int dorawread(char* buf, int count);
+ virtual void dofilter() = 0;
+
+ bool bufavail() { return savecount > 0; }
+ void post(const char* buf, int count);
+ void post(const char* s);
+ void post(char c);
+ virtual void post(string s);
+
+public:
+ infilter(instm* istm, int ibufsize = -1);
+ virtual ~infilter();
+
+ virtual string get_errstmname();
+
+ instm* get_stm() { return stm; }
+ void set_stm(instm* stm);
+};
+
+
+//
+// abstract output filter class
+//
+
+class ptpublic outfilter: public outstm
+{
+protected:
+ outstm* stm;
+ virtual void freenotify(component* sender);
+ virtual void doopen();
+ virtual void doclose();
+
+public:
+ outfilter(outstm* istm, int ibufsize = -1);
+ virtual ~outfilter();
+ virtual string get_errstmname();
+ outstm* get_stm() { return stm; }
+ void set_stm(outstm* stm);
+};
+
+
+//
+// inmemory - memory stream
+//
+
+class ptpublic inmemory: public instm
+{
+protected:
+ string mem;
+ virtual void bufalloc();
+ virtual void buffree();
+ virtual void bufvalidate();
+ virtual void doopen();
+ virtual void doclose();
+ virtual large doseek(large newpos, ioseekmode mode);
+ virtual int dorawread(char* buf, int count);
+
+public:
+ inmemory(const string& imem);
+ virtual ~inmemory();
+ virtual int classid();
+ virtual string get_streamname();
+ large seekx(large newpos, ioseekmode mode = IO_BEGIN);
+ int seek(int newpos, ioseekmode mode = IO_BEGIN) { return convertoffset(seekx(newpos, mode)); }
+ string get_strdata() { return mem; }
+ void set_strdata(const string& data);
+};
+
+
+//
+// outmemory - memory stream
+//
+
+class ptpublic outmemory: public outstm
+{
+protected:
+ string mem;
+ int limit;
+
+ virtual void doopen();
+ virtual void doclose();
+ virtual large doseek(large newpos, ioseekmode mode);
+ virtual int dorawwrite(const char* buf, int count);
+
+public:
+ outmemory(int limit = -1);
+ virtual ~outmemory();
+ virtual int classid();
+ virtual string get_streamname();
+ large tellx() { return abspos; }
+ int tell() { return (int)abspos; }
+ string get_strdata();
+};
+
+
+// -------------------------------------------------------------------- //
+// --- file input/output --------------------------------------------- //
+// -------------------------------------------------------------------- //
+
+
+//
+// infile - file input
+//
+
+class outfile;
+
+class ptpublic infile: public instm
+{
+protected:
+ string filename;
+ int syshandle; // if not -1, assigned to handle in open() instead of opening a file by a name
+ int peerhandle; // pipe peer handle, needed for closing the peer after fork() on unix
+
+ virtual void doopen();
+ virtual void doclose();
+
+public:
+ infile();
+ infile(const char* ifn);
+ infile(const string& ifn);
+ virtual ~infile();
+ virtual int classid();
+
+ void pipe(outfile&);
+ virtual string get_streamname();
+ int get_syshandle() { return syshandle; }
+ void set_syshandle(int ihandle) { close(); syshandle = ihandle; }
+ int get_peerhandle() { return peerhandle; }
+ string get_filename() { return filename; }
+ void set_filename(const string& ifn) { close(); filename = ifn; }
+ void set_filename(const char* ifn) { close(); filename = ifn; }
+};
+
+
+//
+// outfile - file output
+//
+
+class ptpublic outfile: public outstm
+{
+protected:
+ friend class infile; // infile::pipe() needs access to peerhandle
+
+ string filename;
+ int syshandle; // if not -1, assigned to handle in open() instead of opening a file by a name
+ int peerhandle; // pipe peer handle, needed for closing the peer after fork() on unix
+ int umode; // unix file mode (unix only), default = 644
+ bool append; // append (create new if needed), default = false
+
+ virtual void doopen();
+ virtual void doclose();
+
+public:
+ outfile();
+ outfile(const char* ifn, bool iappend = false);
+ outfile(const string& ifn, bool iappend = false);
+ virtual ~outfile();
+ virtual int classid();
+
+ virtual void flush();
+ virtual string get_streamname();
+
+ int get_syshandle() { return syshandle; }
+ void set_syshandle(int ihandle) { close(); syshandle = ihandle; }
+ int get_peerhandle() { return peerhandle; }
+ string get_filename() { return filename; }
+ void set_filename(const string& ifn) { close(); filename = ifn; }
+ void set_filename(const char* ifn) { close(); filename = ifn; }
+ bool get_append() { return append; }
+ void set_append(bool iappend) { close(); append = iappend; }
+ int get_umode() { return umode; }
+ void set_umode(int iumode) { close(); umode = iumode; }
+};
+
+
+//
+// logfile - file output with thread-safe putf()
+//
+
+class ptpublic logfile: public outfile
+{
+protected:
+#ifndef PTYPES_ST
+ mutex lock;
+#endif
+public:
+ logfile();
+ logfile(const char* ifn, bool iappend = true);
+ logfile(const string& ifn, bool iappend = true);
+ virtual ~logfile();
+ virtual int classid();
+
+ void vputf(const char* fmt, va_list);
+ void putf(const char* fmt, ...);
+};
+
+
+//
+// intee - UNIX tee-style utility class
+//
+
+class ptpublic intee: public infilter {
+protected:
+ outfile file;
+ virtual void doopen();
+ virtual void doclose();
+ virtual void dofilter();
+public:
+ intee(instm* istm, const char* ifn, bool iappend = false);
+ intee(instm* istm, const string& ifn, bool iappend = false);
+ virtual ~intee();
+
+ outfile* get_file() { return &file; }
+ virtual string get_streamname();
+};
+
+
+// -------------------------------------------------------------------- //
+// --- named pipes --------------------------------------------------- //
+// -------------------------------------------------------------------- //
+
+
+// on Unix this directory can be overridden by providing the
+// full path, e.g. '/var/run/mypipe'. the path is ignored on
+// Windows and is always replaced with '\\<server>\pipe\'
+
+#ifndef WIN32
+# define DEF_NAMED_PIPES_DIR "/tmp/"
+#endif
+
+
+#ifdef WIN32
+
+const int DEF_PIPE_TIMEOUT = 20000; // in milliseconds, for reading and writing
+const int DEF_PIPE_OPEN_TIMEOUT = 1000; // for connecting to the remote pipe:
+const int DEF_PIPE_OPEN_RETRY = 5; // will double the timeout value for each retry,
+ // i.e. 1 second, then 2, then 4 etc.
+const int DEF_PIPE_SYSTEM_BUF_SIZE = 4096;
+
+#endif
+
+
+class ptpublic namedpipe: public fdxstm
+{
+ friend class npserver;
+
+protected:
+ string pipename;
+ int svhandle;
+
+#ifdef WIN32
+ // we use overlapped IO in order to have timed waiting in serve()
+ // and also to implement timeout error on the client side
+ OVERLAPPED ovr;
+ virtual int dorawread(char* buf, int count);
+ virtual int dorawwrite(const char* buf, int count);
+ static string ptdecl realpipename(const string& pipename, const string& svrname = nullstring);
+ void initovr();
+#else
+ static string realpipename(const string& pipename);
+ static bool setupsockaddr(const string& pipename, void* sa);
+ void initovr() {}
+#endif
+
+ virtual void doopen();
+ virtual void doclose();
+ virtual large doseek(large, ioseekmode);
+
+public:
+ namedpipe();
+ namedpipe(const string& ipipename);
+#ifdef WIN32
+ namedpipe(const string& ipipename, const string& servername);
+#endif
+ virtual ~namedpipe();
+ virtual int classid();
+
+ virtual void flush();
+ virtual string get_streamname();
+
+ string get_pipename() { return pipename; }
+ void set_pipename(const string&);
+ void set_pipename(const char*);
+};
+
+
+class ptpublic npserver: public unknown
+{
+ string pipename;
+ int handle;
+ bool active;
+
+ void error(int code, const char* defmsg);
+ void open();
+ void close();
+#ifdef WIN32
+ void openinst();
+ void closeinst();
+#endif
+
+public:
+ npserver(const string& ipipename);
+ ~npserver();
+
+ bool serve(namedpipe& client, int timeout = -1);
+};
+
+
+// -------------------------------------------------------------------- //
+// --- utility streams ----------------------------------------------- //
+// -------------------------------------------------------------------- //
+
+//
+// MD5 -- message digest algorithm
+// Derived from L. Peter Deutsch's work, please see src/pmd5.cxx
+//
+
+
+const int md5_digsize = 16;
+typedef uchar md5_digest[md5_digsize];
+
+// from md5.h
+
+typedef unsigned char md5_byte_t; /* 8-bit byte */
+typedef unsigned int md5_word_t; /* 32-bit word */
+
+
+typedef struct md5_state_s
+{
+ md5_word_t count[2]; /* message length in bits, lsw first */
+ md5_word_t abcd[4]; /* digest buffer */
+ md5_byte_t buf[64]; /* accumulate block */
+} md5_state_t;
+
+
+class ptpublic outmd5: public outfilter
+{
+protected:
+ md5_state_s ctx;
+ md5_digest digest;
+
+ virtual void doopen();
+ virtual void doclose();
+ virtual int dorawwrite(const char* buf, int count);
+
+public:
+ outmd5(outstm* istm = nil);
+ virtual ~outmd5();
+
+ virtual string get_streamname();
+
+ const unsigned char* get_bindigest() { close(); return digest; }
+ string get_digest();
+};
+
+
+//
+// null output stream
+//
+
+
+class ptpublic outnull: public outstm
+{
+protected:
+ virtual int dorawwrite(const char*, int);
+ virtual void doopen();
+ virtual void doclose();
+public:
+ outnull();
+ virtual ~outnull();
+ virtual string get_streamname();
+};
+
+
+// -------------------------------------------------------------------- //
+// --- unit ---------------------------------------------------------- //
+// -------------------------------------------------------------------- //
+
+
+#ifdef _MSC_VER
+// disable "type name first seen using 'struct' now seen using 'class'" warning
+# pragma warning (disable: 4099)
+// disable "class '...' needs to have dll-interface to be used by clients of class
+// '...'" warning, since the compiler may sometimes give this warning incorrectly.
+# pragma warning (disable: 4251)
+#endif
+
+class unit_thread;
+
+class ptpublic unit: public component
+{
+protected:
+ friend class unit_thread;
+
+ unit* pipe_next; // next unit in the pipe chain, assigned by connect()
+ unit_thread* main_thread; // async execution thread, started by run() if necessary
+ int running; // running status, to protect from recursive calls to run() and waitfor()
+
+ void do_main();
+
+public:
+ compref<instm> uin;
+ compref<outstm> uout;
+
+ unit();
+ virtual ~unit();
+ virtual int classid();
+
+ // things that may be overridden in descendant classes
+ virtual void main(); // main code, called from run()
+ virtual void cleanup(); // main code cleanup, called from run()
+
+ // service methods
+ void connect(unit* next);
+ void run(bool async = false);
+ void waitfor();
+};
+typedef unit* punit;
+
+
+typedef unit CUnit; // send me a $10 check if you use this alias (not obligatory though,
+ // because the library is free, after all)
+
+
+//
+// standard input, output and error devices
+//
+
+ptpublic extern infile pin;
+ptpublic extern logfile pout;
+ptpublic extern logfile perr;
+ptpublic extern outnull pnull;
+
+
+#ifdef _MSC_VER
+#pragma pack(pop)
+#endif
+
+
+}
+
+#endif // __PSTREAMS_H__
+
diff --git a/source/ptypes/pstring.cxx b/source/ptypes/pstring.cxx
@@ -0,0 +1,289 @@
+/*
+ *
+ * C++ Portable Types Library (PTypes)
+ * Version 2.1.1 Released 27-Jun-2007
+ *
+ * Copyright (C) 2001-2007 Hovik Melikyan
+ *
+ * http://www.melikyan.com/ptypes/
+ *
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include "ptypes.h"
+#include "ptime.h" // nowstring() is defined in this module
+
+
+namespace ptypes {
+
+
+const int strrecsize = sizeof(_strrec);
+
+
+static void stringoverflow()
+{
+ fatal(CRIT_FIRST + 21, "String overflow");
+}
+
+
+void string::idxerror()
+{
+ fatal(CRIT_FIRST + 20, "String index overflow");
+}
+
+
+int stralloc;
+
+char emptystrbuf[strrecsize + 4];
+char* emptystr = emptystrbuf + strrecsize;
+
+
+string nullstring;
+
+
+inline int quantize(int numchars)
+{
+ return memquantize(numchars + 1 + strrecsize);
+}
+
+
+void string::_alloc(int numchars)
+{
+ if (numchars <= 0)
+ stringoverflow();
+ size_t a = quantize(numchars);
+#ifdef DEBUG
+ stralloc += a;
+#endif
+ data = (char*)(memalloc(a)) + strrecsize;
+ STR_LENGTH(data) = numchars;
+ STR_REFCOUNT(data) = 1;
+ data[numchars] = 0;
+}
+
+
+void string::_realloc(int numchars)
+{
+ if (numchars <= 0 || STR_LENGTH(data) <= 0)
+ stringoverflow();
+ int a = quantize(numchars);
+ int b = quantize(STR_LENGTH(data));
+ if (a != b)
+ {
+#ifdef DEBUG
+ stralloc += a - b;
+#endif
+ data = (char*)(memrealloc(data - strrecsize, a)) + strrecsize;
+ }
+ STR_LENGTH(data) = numchars;
+ data[numchars] = 0;
+}
+
+
+inline void _freestrbuf(char* data)
+{
+#ifdef DEBUG
+ stralloc -= quantize(STR_LENGTH(data));
+#endif
+ memfree((char*)(STR_BASE(data)));
+}
+
+
+void string::_free()
+{
+ _freestrbuf(data);
+ data = emptystr;
+}
+
+
+void string::initialize(const char* sc, int initlen)
+{
+ if (initlen <= 0 || sc == nil)
+ data = emptystr;
+ else
+ {
+ _alloc(initlen);
+ memmove(data, sc, initlen);
+ }
+}
+
+
+void string::initialize(const char* sc)
+{
+ initialize(sc, hstrlen(sc));
+}
+
+
+void string::initialize(char c)
+{
+ _alloc(1);
+ data[0] = c;
+}
+
+
+void string::initialize(const string& s)
+{
+ data = s.data;
+#ifdef PTYPES_ST
+ STR_REFCOUNT(data)++;
+#else
+ pincrement(&STR_REFCOUNT(data));
+#endif
+}
+
+
+void string::finalize()
+{
+ if (STR_LENGTH(data) != 0)
+ {
+
+#ifdef PTYPES_ST
+ if (--STR_REFCOUNT(data) == 0)
+#else
+ if (pdecrement(&STR_REFCOUNT(data)) == 0)
+#endif
+ _freestrbuf(data);
+
+ data = emptystr;
+ }
+}
+
+
+char* ptdecl unique(string& s)
+{
+ if (STR_LENGTH(s.data) > 0 && STR_REFCOUNT(s.data) > 1)
+ {
+ char* odata = s.data;
+ s._alloc(STR_LENGTH(s.data));
+ memcpy(s.data, odata, STR_LENGTH(s.data));
+#ifdef PTYPES_ST
+ STR_REFCOUNT(odata)--;
+#else
+ if (pdecrement(&STR_REFCOUNT(odata)) == 0)
+ _freestrbuf(odata);
+#endif
+ }
+ return s.data;
+}
+
+
+char* ptdecl setlength(string& s, int newlen)
+{
+ if (newlen < 0)
+ return nil;
+
+ int curlen = STR_LENGTH(s.data);
+
+ // if becoming empty
+ if (newlen == 0)
+ s.finalize();
+
+ // if otherwise s was empty before
+ else if (curlen == 0)
+ s._alloc(newlen);
+
+ // if length is not changing, return a unique string
+ else if (newlen == curlen)
+ unique(s);
+
+ // non-unique reallocation
+ else if (STR_REFCOUNT(s.data) > 1)
+ {
+ char* odata = s.data;
+ s._alloc(newlen);
+ memcpy(s.data, odata, imin(curlen, newlen));
+#ifdef PTYPES_ST
+ STR_REFCOUNT(odata)--;
+#else
+ if (pdecrement(&STR_REFCOUNT(odata)) == 0)
+ _freestrbuf(odata);
+#endif
+ }
+
+ // unique reallocation
+ else
+ s._realloc(newlen);
+
+ return s.data;
+}
+
+
+void string::assign(const char* sc, int initlen)
+{
+ if (STR_LENGTH(data) > 0 && initlen > 0 && STR_REFCOUNT(data) == 1)
+ {
+ // reuse data buffer if unique
+ _realloc(initlen);
+ memmove(data, sc, initlen);
+ }
+ else
+ {
+ finalize();
+ if (initlen == 1)
+ initialize(sc[0]);
+ else if (initlen > 1)
+ initialize(sc, initlen);
+ }
+}
+
+
+void string::assign(const char* sc)
+{
+ assign(sc, hstrlen(sc));
+}
+
+
+void string::assign(char c)
+{
+ assign(&c, 1);
+}
+
+
+void string::assign(const string& s)
+{
+ if (data != s.data)
+ {
+ finalize();
+ initialize(s);
+ }
+}
+
+
+string ptdecl dup(const string& s)
+{
+ // dup() only reads the data pointer so it is thread-safe
+ return string(s.data);
+}
+
+
+string ptdecl nowstring(const char* fmt, bool utc)
+{
+ char buf[128];
+ time_t longtime;
+ time(&longtime);
+
+#if defined(PTYPES_ST) || defined(WIN32)
+ tm* t;
+ if (utc)
+ t = gmtime(&longtime);
+ else
+ t = localtime(&longtime);
+ int r = strftime(buf, sizeof(buf), fmt, t);
+#else
+ tm t;
+ if (utc)
+ gmtime_r(&longtime, &t);
+ else
+ localtime_r(&longtime, &t);
+ int r = strftime(buf, sizeof(buf), fmt, &t);
+#endif
+
+ buf[r] = 0;
+ return string(buf);
+}
+
+
+}
diff --git a/source/ptypes/pstrlist.cxx b/source/ptypes/pstrlist.cxx
@@ -0,0 +1,204 @@
+/*
+ *
+ * C++ Portable Types Library (PTypes)
+ * Version 2.1.1 Released 27-Jun-2007
+ *
+ * Copyright (C) 2001-2007 Hovik Melikyan
+ *
+ * http://www.melikyan.com/ptypes/
+ *
+ */
+
+#include "ptypes.h"
+
+
+namespace ptypes {
+
+
+typedef _stritem* pstritem;
+
+
+void _strlist::sortederror()
+{
+ fatal(CRIT_FIRST + 32, "Operation not allowed on sorted string lists");
+}
+
+
+void _strlist::notsortederror()
+{
+ fatal(CRIT_FIRST + 33, "Search only allowed on sorted string lists");
+}
+
+
+void _strlist::duperror()
+{
+ fatal(CRIT_FIRST + 34, "Duplicate items not allowed in this string list");
+}
+
+
+_strlist::_strlist(int flags)
+ : tobjlist<_stritem>(true)
+{
+ if ((flags & SL_SORTED) != 0)
+ config.sorted = 1;
+ if ((flags & SL_DUPLICATES) != 0)
+ config.duplicates = 1;
+ if ((flags & SL_CASESENS) != 0)
+ config.casesens = 1;
+ if ((flags & SL_OWNOBJECTS) != 0)
+ config.ownslobjects = 1;
+}
+
+
+_strlist::~_strlist()
+{
+}
+
+
+void _strlist::dofree(void* item)
+{
+ if (config.ownslobjects)
+ dofreeobj(pstritem(item)->obj);
+ delete pstritem(item);
+}
+
+
+void _strlist::dofreeobj(void*)
+{
+ fatal(CRIT_FIRST + 38, "strlist::dofree() not defined");
+}
+
+
+int _strlist::compare(const void* key, const void* item) const
+{
+ if (config.casesens)
+ return strcmp(pconst(key), pstritem(item)->key);
+ else
+ return strcasecmp(pconst(key), pstritem(item)->key);
+}
+
+
+void _strlist::doins(int index, const string& key, void* obj)
+{
+ tobjlist<_stritem>::ins(index, new _stritem(key, obj));
+}
+
+
+void _strlist::doput(int index, const string& key, void* obj)
+{
+ if (config.sorted)
+ sortederror();
+ _stritem* p = doget(index);
+ if (config.ownslobjects)
+ dofreeobj(p->obj);
+ p->key = key;
+ p->obj = obj;
+}
+
+
+void _strlist::doput(int index, void* obj)
+{
+ _stritem* p = doget(index);
+ if (config.ownslobjects)
+ dofreeobj(p->obj);
+ p->obj = obj;
+}
+
+
+int _strlist::put(const string& key, void* obj)
+{
+ if (!config.sorted)
+ notsortederror();
+ if (config.duplicates)
+ duperror();
+ int index;
+ if (search(key, index))
+ {
+ if (obj == nil)
+ dodel(index);
+ else
+ doput(index, obj);
+ }
+ else if (obj != nil)
+ doins(index, key, obj);
+ return index;
+}
+
+
+int _strlist::add(const string& key, void* obj)
+{
+ int index;
+ if (config.sorted)
+ {
+ if (search(key, index) && !config.duplicates)
+ duperror();
+ }
+ else
+ index = count;
+ doins(index, key, obj);
+ return index;
+}
+
+
+void* _strlist::operator [](const char* key) const
+{
+ if (!config.sorted)
+ notsortederror();
+ int index;
+ if (search(key, index))
+ return dogetobj(index);
+ else
+ return nil;
+}
+
+
+int _strlist::indexof(const char* key) const
+{
+ if (config.sorted)
+ {
+ int index;
+ if (search(key, index))
+ return index;
+ }
+ else
+ {
+ for (int i = 0; i < count; i++)
+ if (compare(key, doget(i)) == 0)
+ return i;
+ }
+ return -1;
+}
+
+
+int _strlist::indexof(void* obj) const
+{
+ for (int i = 0; i < count; i++)
+ if (pstritem(doget(i))->obj == obj)
+ return i;
+ return -1;
+}
+
+
+//
+// strmap
+//
+
+#ifdef PTYPES19_COMPAT
+
+strlist::strlist(int flags): tstrlist<unknown>(flags) {}
+
+strlist::~strlist() {}
+
+strmap::strmap(int flags)
+ : tstrlist<unknown>((flags | SL_SORTED) & ~SL_DUPLICATES)
+{
+}
+
+strmap::~strmap()
+{
+}
+
+#endif
+
+
+}
diff --git a/source/ptypes/pstrmanip.cxx b/source/ptypes/pstrmanip.cxx
@@ -0,0 +1,281 @@
+/*
+ *
+ * C++ Portable Types Library (PTypes)
+ * Version 2.1.1 Released 27-Jun-2007
+ *
+ * Copyright (C) 2001-2007 Hovik Melikyan
+ *
+ * http://www.melikyan.com/ptypes/
+ *
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h> // for INT_MAX
+
+#include "ptypes.h"
+
+
+namespace ptypes {
+
+
+void string::initialize(const char* s1, int len1, const char* s2, int len2)
+{
+ if (len1 <= 0)
+ initialize(s2, len2);
+ else if (len2 <= 0)
+ initialize(s1, len1);
+ else
+ {
+ _alloc(len1 + len2);
+ memcpy(data, s1, len1);
+ memcpy(data + len1, s2, len2);
+ }
+}
+
+
+void ptdecl concat(string& s, const char* sc, int catlen)
+{
+ if (length(s) == 0)
+ s.assign(sc, catlen);
+ else if (catlen > 0)
+ {
+ int oldlen = length(s);
+
+ // we must check this before calling setlength(), since
+ // the buffer pointer may be changed during reallocation
+ if (s.data == sc)
+ {
+ setlength(s, oldlen + catlen);
+ memmove(s.data + oldlen, s.data, catlen);
+ }
+ else
+ {
+ setlength(s, oldlen + catlen);
+ memmove(s.data + oldlen, sc, catlen);
+ }
+ }
+}
+
+
+void ptdecl concat(string& s, const char* sc)
+{
+ concat(s, sc, hstrlen(sc));
+}
+
+
+void ptdecl concat(string& s, char c)
+{
+ if (length(s) == 0)
+ s.assign(c);
+ else
+ {
+ setlength(s, length(s) + 1);
+ s.data[length(s) - 1] = c;
+ }
+}
+
+
+void ptdecl concat(string& s, const string& s1)
+{
+ if (length(s) == 0)
+ s = s1;
+ else if (length(s1) > 0)
+ concat(s, s1.data, length(s1));
+}
+
+
+bool ptdecl contains(const char* s1, int s1len, const string& s, int at)
+{
+ return (s1len >= 0) && (at >= 0) && (at + s1len <= length(s))
+ && (s1len == 0 || memcmp(s.data + at, s1, s1len) == 0);
+}
+
+
+bool ptdecl contains(const char* s1, const string& s, int at)
+{
+ return contains(s1, hstrlen(s1), s, at);
+}
+
+
+bool ptdecl contains(char s1, const string& s, int at)
+{
+ return (at >= 0) && (at < length(s)) && (s.data[at] == s1);
+}
+
+
+bool ptdecl contains(const string& s1, const string& s, int at)
+{
+ return contains(s1.data, length(s1), s, at);
+}
+
+
+string string::operator+ (const char* sc) const
+{
+ if (length(*this) == 0)
+ return string(sc);
+ else
+ return string(data, length(*this), sc, hstrlen(sc));
+}
+
+
+string string::operator+ (char c) const
+{
+ if (length(*this) == 0)
+ return string(c);
+ else
+ return string(data, length(*this), &c, 1);
+}
+
+
+string string::operator+ (const string& s) const
+{
+ if (length(*this) == 0)
+ return s;
+ else if (length(s) == 0)
+ return *this;
+ else
+ return string(data, length(*this), s.data, length(s));
+}
+
+
+string ptdecl operator+ (const char* sc, const string& s)
+{
+ if (length(s) == 0)
+ return string(sc);
+ else
+ return string(sc, hstrlen(sc), s.data, length(s));
+}
+
+
+string ptdecl operator+ (char c, const string& s)
+{
+ if (length(s) == 0)
+ return string(c);
+ else
+ return string(&c, 1, s.data, length(s));
+}
+
+
+bool string::operator== (const string& s) const
+{
+ return (length(*this) == length(s))
+ && ((length(*this) == 0) || (memcmp(data, s.data, length(*this)) == 0));
+}
+
+
+bool string::operator== (char c) const
+{
+ return (length(*this) == 1) && (data[0] == c);
+}
+
+
+string ptdecl copy(const string& s, int from, int cnt)
+{
+ string t;
+ if (length(s) > 0 && from >= 0 && from < length(s))
+ {
+ int l = imin(cnt, length(s) - from);
+ if (from == 0 && l == length(s))
+ t = s;
+ else if (l > 0)
+ {
+ t._alloc(l);
+ memmove(t.data, s.data + from, l);
+ t.data[l] = 0;
+ }
+ }
+ return t;
+}
+
+
+string ptdecl copy(const string& s, int from)
+{
+ return copy(s, from, INT_MAX);
+}
+
+
+void ptdecl ins(const char* s1, int s1len, string& s, int at)
+{
+ int curlen = length(s);
+ if (s1len > 0 && at >= 0 && at <= curlen)
+ {
+ if (curlen == 0)
+ s.assign(s1, s1len);
+ else
+ {
+ setlength(s, curlen + s1len);
+ int t = length(s) - at - s1len;
+ char* p = s.data + at;
+ if (t > 0)
+ memmove(p + s1len, p, t);
+ memmove(p, s1, s1len);
+ }
+ }
+}
+
+
+void ptdecl ins(const char* sc, string& s, int at)
+{
+ ins(sc, hstrlen(sc), s, at);
+}
+
+
+void ptdecl ins(char c, string& s, int at)
+{
+ ins(&c, 1, s, at);
+}
+
+
+void ptdecl ins(const string& s1, string& s, int at)
+{
+ ins(s1.data, length(s1), s, at);
+}
+
+
+void ptdecl del(string& s, int from, int cnt)
+{
+ int l = length(s);
+ int d = l - from;
+ if (from >= 0 && d > 0 && cnt > 0)
+ {
+ if (cnt < d)
+ {
+ unique(s);
+ memmove(s.data + from, s.data + from + cnt, d - cnt);
+ }
+ else
+ cnt = d;
+ setlength(s, l - cnt);
+ }
+}
+
+
+void ptdecl del(string& s, int from)
+{
+ setlength(s, from);
+}
+
+
+int ptdecl pos(const char* sc, const string& s)
+{
+ const char* t = (char*)strstr(s.data, sc);
+ return (t == NULL ? (-1) : int(t - s.data));
+}
+
+
+int ptdecl pos(char c, const string& s)
+{
+ const char* t = (char*)strchr(s.data, c);
+ return (t == NULL ? (-1) : int(t - s.data));
+}
+
+
+int ptdecl rpos(char c, const string& s)
+{
+ const char* t = (char*)strrchr(s.data, c);
+ return (t == NULL ? (-1) : int(t - s.data));
+}
+
+
+}
diff --git a/source/ptypes/pstrtoi.cxx b/source/ptypes/pstrtoi.cxx
@@ -0,0 +1,136 @@
+/*
+ *
+ * C++ Portable Types Library (PTypes)
+ * Version 2.1.1 Released 27-Jun-2007
+ *
+ * Copyright (C) 2001-2007 Hovik Melikyan
+ *
+ * http://www.melikyan.com/ptypes/
+ *
+ */
+
+#include <string.h>
+
+#include "ptypes.h"
+
+
+namespace ptypes {
+
+
+static void throw_conv(const char* p);
+static void throw_overflow(const char* p);
+
+
+large ptdecl stringtoi(const char* p)
+{
+ if (p == 0)
+ return -1;
+ if (*p == 0)
+ return -1;
+
+ large r = 0;
+ do
+ {
+ char c = *p++;
+ if (c < '0' || c > '9')
+ return -1; // invalid character
+ large t = r * 10;
+ if (t < r)
+ return -1; // overflow
+ t += c - '0';
+ if (t < r)
+ return -1; // overflow
+ r = t;
+ } while (*p != 0);
+
+ return r;
+}
+
+
+econv::~econv()
+{
+}
+
+
+ularge ptdecl stringtoue(const char* str, int base)
+{
+ if (str == 0)
+ throw_conv(str);
+ if (*str == 0 || base < 2 || base > 64)
+ throw_conv(str);
+
+ const char* p = str;
+ ularge result = 0;
+
+ do
+ {
+ int c = *p++;
+
+ if (c >= 'a')
+ {
+ // for the numeration bases that use '.', '/', digits and
+ // uppercase letters the letter case is insignificant.
+ if (base <= 38)
+ c -= 'a' - '9' - 1;
+ else // others use both upper and lower case letters
+ c -= ('a' - 'Z' - 1) + ('A' - '9' - 1);
+ }
+ else if (c > 'Z')
+ throw_conv(str);
+ else if (c >= 'A')
+ c -= 'A' - '9' - 1;
+ else if (c > '9')
+ throw_conv(str);
+
+ c -= (base > 36) ? '.' : '0';
+ if (c < 0 || c >= base)
+ throw_conv(str);
+
+ ularge t = result * uint(base);
+ if (t / base != result)
+ throw_overflow(str);
+ result = t;
+ t = result + uint(c);
+ if (t < result)
+ throw_overflow(str);
+ result = t;
+ } while (*p != 0);
+
+ return result;
+}
+
+
+large ptdecl stringtoie(const char* str)
+{
+ if (str == 0)
+ throw_conv(str);
+ bool neg = *str == '-';
+ ularge result = stringtoue(str + int(neg), 10);
+ if (result > (ularge(LARGE_MAX) + uint(neg)))
+ throw_overflow(str);
+ if (neg)
+ return - large(result);
+ else
+ return large(result);
+}
+
+
+#ifdef _MSC_VER
+// disable "unreachable code" warning for throw (known compiler bug)
+# pragma warning (disable: 4702)
+#endif
+
+static void throw_conv(const char* p)
+{
+ throw new econv("Invalid number: '" + string(p) + '\'');
+}
+
+
+static void throw_overflow(const char* p)
+{
+ throw new econv("Out of range: '" + string(p) + '\'');
+}
+
+
+}
+
diff --git a/source/ptypes/pstrutils.cxx b/source/ptypes/pstrutils.cxx
@@ -0,0 +1,57 @@
+/*
+ *
+ * C++ Portable Types Library (PTypes)
+ * Version 2.1.1 Released 27-Jun-2007
+ *
+ * Copyright (C) 2001-2007 Hovik Melikyan
+ *
+ * http://www.melikyan.com/ptypes/
+ *
+ */
+
+#include <string.h>
+
+#include "ptypes.h"
+
+
+namespace ptypes {
+
+
+string ptdecl fill(int width, char pad)
+{
+ string res;
+ if (width > 0) {
+ setlength(res, width);
+ memset(pchar(pconst(res)), pad, length(res));
+ }
+ return res;
+}
+
+
+string ptdecl pad(const string& s, int width, char c, bool left)
+{
+ int len = length(s);
+ if (len < width && width > 0)
+ {
+ string res;
+ setlength(res, width);
+ if (left)
+ {
+ if (len > 0)
+ memcpy(pchar(pconst(res)), pconst(s), len);
+ memset(pchar(pconst(res)) + len, c, width - len);
+ }
+ else
+ {
+ memset(pchar(pconst(res)), c, width - len);
+ if (len > 0)
+ memcpy(pchar(pconst(res)) + width - len, pconst(s), len);
+ }
+ return res;
+ }
+ else
+ return s;
+}
+
+
+}
diff --git a/source/ptypes/ptextmap.cxx b/source/ptypes/ptextmap.cxx
@@ -0,0 +1,79 @@
+/*
+ *
+ * C++ Portable Types Library (PTypes)
+ * Version 2.1.1 Released 27-Jun-2007
+ *
+ * Copyright (C) 2001-2007 Hovik Melikyan
+ *
+ * http://www.melikyan.com/ptypes/
+ *
+ */
+
+#include "ptypes.h"
+
+
+namespace ptypes {
+
+
+typedef _textitem* ptextitem;
+
+
+textmap::textmap(bool casesens)
+ : tobjlist<_textitem>(true)
+{
+ config.sorted = true;
+ config.casesens = casesens;
+}
+
+
+textmap::~textmap()
+{
+}
+
+
+int textmap::compare(const void* key, const void* item) const
+{
+ if (config.casesens)
+ return strcmp(pconst(key), ptextitem(item)->key);
+ else
+ return strcasecmp(pconst(key), ptextitem(item)->key);
+}
+
+
+const string& textmap::get(const char* key) const
+{
+ int index;
+ if (search(key, index))
+ return dogetvalue(index);
+ else
+ return nullstring;
+}
+
+
+int textmap::put(const string& key, const string& value)
+{
+ int index;
+ if (search(pconst(key), index))
+ {
+ if (isempty(value))
+ dodel(index);
+ else
+ doget(index)->value = value;
+ }
+ else if (!isempty(value))
+ doins(index, new _textitem(key, value));
+ return index;
+}
+
+
+int textmap::indexof(const char* key) const
+{
+ int index;
+ if (search(key, index))
+ return index;
+ else
+ return -1;
+}
+
+
+}
diff --git a/source/ptypes/pthread.cxx b/source/ptypes/pthread.cxx
@@ -0,0 +1,154 @@
+/*
+ *
+ * C++ Portable Types Library (PTypes)
+ * Version 2.1.1 Released 27-Jun-2007
+ *
+ * Copyright (C) 2001-2007 Hovik Melikyan
+ *
+ * http://www.melikyan.com/ptypes/
+ *
+ */
+
+#ifdef WIN32
+# include <process.h>
+#else
+# include <pthread.h>
+#endif
+
+#include "pasync.h"
+
+
+namespace ptypes {
+
+
+thread::thread(bool iautofree)
+ :
+#ifdef WIN32
+ id(0),
+#endif
+ handle(0), autofree(iautofree),
+ running(0), signaled(0), finished(0), freed(0),
+ reserved(0), relaxsem(0)
+{
+}
+
+
+thread::~thread()
+{
+ if (pexchange(&freed, 1) != 0)
+ return;
+#ifdef WIN32
+ if (autofree)
+ // MSDN states this is not necessary, however, without closing
+ // the handle debuggers show an obvious handle leak here
+ CloseHandle(handle);
+#else
+ // though we require non-autofree threads to always call waitfor(),
+ // the statement below is provided to cleanup thread resources even
+ // if waitfor() was not called.
+ if (!autofree && running)
+ pthread_detach(handle);
+#endif
+}
+
+
+void thread::signal()
+{
+ if (pexchange(&signaled, 1) == 0)
+ relaxsem.post();
+}
+
+
+void thread::waitfor()
+{
+ if (pexchange(&freed, 1) != 0)
+ return;
+ if (pthrequal(get_id()))
+ fatal(CRIT_FIRST + 47, "Can not waitfor() on myself");
+ if (autofree)
+ fatal(CRIT_FIRST + 48, "Can not waitfor() on an autofree thread");
+#ifdef WIN32
+ WaitForSingleObject(handle, INFINITE);
+ CloseHandle(handle);
+#else
+ pthread_join(handle, nil);
+// detaching after 'join' is not required (or even do harm on some systems)
+// except for HPUX. we don't support HPUX yet.
+// pthread_detach(handle);
+#endif
+ handle = 0;
+}
+
+
+#ifdef WIN32
+unsigned _stdcall _threadproc(void* arg)
+{
+#else
+void* _threadproc(void* arg)
+{
+#endif
+ thread* thr = (thread*)arg;
+#ifndef WIN32
+ if (thr->autofree)
+ // start() does not assign the handle for autofree threads
+ thr->handle = pthread_self();
+#endif
+ try
+ {
+ thr->execute();
+ }
+ catch(exception*)
+ {
+ _threadepilog(thr);
+ throw;
+ }
+ _threadepilog(thr);
+ return 0;
+}
+
+
+void _threadepilog(thread* thr)
+{
+ try
+ {
+ thr->cleanup();
+ }
+ catch(exception* e)
+ {
+ delete e;
+ }
+ pexchange(&thr->finished, 1);
+ if (thr->autofree)
+ delete thr;
+}
+
+
+void thread::start()
+{
+ if (pexchange(&running, 1) == 0)
+ {
+#ifdef WIN32
+ handle = (HANDLE)_beginthreadex(nil, 0, _threadproc, this, 0, &id);
+ if (handle == 0)
+ fatal(CRIT_FIRST + 40, "CreateThread() failed");
+#else
+ pthread_t temp_handle;
+ pthread_attr_t attr;
+ pthread_attr_init(&attr);
+ pthread_attr_setdetachstate(&attr,
+ autofree ? PTHREAD_CREATE_DETACHED : PTHREAD_CREATE_JOINABLE);
+ if (pthread_create(autofree ? &temp_handle : &handle,
+ &attr, _threadproc, this) != 0)
+ fatal(CRIT_FIRST + 40, "pthread_create() failed");
+ pthread_attr_destroy(&attr);
+#endif
+ }
+}
+
+
+void thread::cleanup()
+{
+}
+
+
+}
diff --git a/source/ptypes/ptime.cxx b/source/ptypes/ptime.cxx
@@ -0,0 +1,325 @@
+/*
+ *
+ * C++ Portable Types Library (PTypes)
+ * Version 2.1.1 Released 27-Jun-2007
+ *
+ * Copyright (C) 2001-2007 Hovik Melikyan
+ *
+ * http://www.melikyan.com/ptypes/
+ *
+ */
+
+// altzone fix for Sun/GCC 3.2. any better ideas?
+#if defined(__sun__) && defined(__GNUC__) && defined(_XOPEN_SOURCE)
+# undef _XOPEN_SOURCE
+#endif
+
+#ifdef WIN32
+# include <windows.h>
+#else
+# include <sys/time.h>
+#endif
+
+#include <time.h>
+#include <string.h>
+
+#include "ptime.h"
+
+
+namespace ptypes {
+
+
+datetime ptdecl mkdt(int days, int msecs)
+{
+ return large(days) * _msecsmax + msecs;
+}
+
+
+bool ptdecl isvalid(datetime d)
+{
+ return d >= 0 && d < _datetimemax;
+}
+
+
+bool ptdecl isleapyear(int year)
+{
+ return year > 0 && year % 4 == 0
+ && (year % 100 != 0 || year % 400 == 0);
+}
+
+
+int ptdecl daysinmonth(int year, int month)
+{
+ if (month < 1 || month > 12)
+ return 0;
+ static const int mdays[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
+ int res = mdays[month - 1];
+ if (month == 2)
+ if (isleapyear(year))
+ res++;
+ return res;
+}
+
+
+int ptdecl daysinyear(int year, int month)
+{
+ if (month < 1 || month > 12)
+ return 0;
+ static const int ndays[12] = {31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365};
+ int res = ndays[month - 1];
+ if (month > 1)
+ if (isleapyear(year))
+ res++;
+ return res;
+}
+
+
+int ptdecl dayofweek(datetime d)
+{
+ return (days(d) + 1) % 7;
+}
+
+
+bool ptdecl isdatevalid(int year, int month, int day)
+{
+ return year >= 1 && year <= 9999
+ && month >= 1 && month <= 12
+ && day >= 1 && day <= daysinmonth(year, month);
+}
+
+
+datetime ptdecl encodedate(int year, int month, int day)
+{
+ if (!isdatevalid(year, month, day))
+ return invdatetime;
+ int y = year - 1;
+ return mkdt(day // days in this month
+ + daysinyear(year, month - 1) // plus days since the beginning of the year
+ + y * 365 // plus "pure" days
+ + y / 4 - y / 100 + y / 400 // plus leap year correction
+ - 1, 0); // ... minus one (guess why :)
+}
+
+
+bool ptdecl decodedate(datetime date, int& year, int& month, int& day)
+{
+ int d = days(date);
+ if (d < 0 || d >= _daysmax) // allowed date range is 01/01/0001 - 12/31/9999
+ {
+ year = 0;
+ month = 0;
+ day = 0;
+ return false;
+ }
+
+ const int d1 = 365; // number of days in 1 year
+ const int d4 = d1 * 4 + 1; // ... in 4 year period
+ const int d100 = d4 * 25 - 1; // ... in 100 year period
+ const int d400 = d100 * 4 + 1; // ... in 400 year period
+
+ year = (d / d400) * 400 + 1;
+ d %= d400;
+
+ int t = d / d100;
+ d %= d100;
+ if (t == 4)
+ {
+ t--;
+ d += d100;
+ }
+ year += t * 100;
+
+ year += (d / d4) * 4;
+ d %= d4;
+
+ t = d / d1;
+ d %= d1;
+ if (t == 4)
+ {
+ t--;
+ d += d1;
+ }
+ year += t;
+
+ month = d / 29; // approximate month no. (to avoid loops)
+ if (d < daysinyear(year, month)) // month no. correction
+ month--;
+
+ day = d - daysinyear(year, month) + 1;
+ month++;
+ return true;
+}
+
+
+bool ptdecl istimevalid(int hour, int min, int sec, int msec)
+{
+ return hour >= 0 && hour < 24
+ && min >= 0 && min < 60
+ && sec >= 0 && sec < 60
+ && msec >= 0 && msec < 1000;
+}
+
+
+datetime ptdecl encodetime(int hour, int min, int sec, int msec)
+{
+ large res = large(hour) * 3600000 + large(min) * 60000 + large(sec) * 1000 + msec;
+ if (!isvalid(res))
+ res = invdatetime;
+ return res;
+}
+
+
+bool ptdecl decodetime(datetime t, int& hour, int& min, int& sec, int& msec)
+{
+ if (!isvalid(t))
+ {
+ hour = 0;
+ min = 0;
+ sec = 0;
+ msec = 0;
+ return false;
+ }
+ int m = msecs(t);
+ hour = m / 3600000;
+ m %= 3600000;
+ min = m / 60000;
+ m %= 60000;
+ sec = m / 1000;
+ msec = m % 1000;
+ return true;
+}
+
+
+bool ptdecl decodetime(datetime t, int& hour, int& min, int& sec)
+{
+ int msec;
+ return decodetime(t, hour, min, sec, msec);
+}
+
+
+tm* ptdecl dttotm(datetime dt, tm& t)
+{
+ memset(&t, 0, sizeof(tm));
+ if (!decodedate(dt, t.tm_year, t.tm_mon, t.tm_mday)
+ || !decodetime(dt, t.tm_hour, t.tm_min, t.tm_sec))
+ return nil;
+ t.tm_mon--;
+ t.tm_yday = daysinyear(t.tm_year, t.tm_mon) + t.tm_mday - 1;
+ t.tm_wday = dayofweek(dt);
+ t.tm_year -= 1900;
+ return &t;
+}
+
+
+string ptdecl dttostring(datetime dt, const char* fmt)
+{
+ char buf[128];
+ tm t;
+ int r = (int)strftime(buf, sizeof(buf), fmt, dttotm(dt, t));
+ buf[r] = 0;
+ return string(buf);
+}
+
+
+datetime ptdecl now(bool utc)
+{
+#ifdef WIN32
+ SYSTEMTIME t;
+ if (utc)
+ GetSystemTime(&t);
+ else
+ GetLocalTime(&t);
+ return encodedate(t.wYear, t.wMonth, t.wDay) +
+ encodetime(t.wHour, t.wMinute, t.wSecond, t.wMilliseconds);
+
+#else // Unix
+ // we can't use localtime() and gmtime() here as they don't return
+ // milliseconds which are needed for our datetime format. instead,
+ // we call gettimeofday() which have microsecond precision, and then
+ // adjust the time according to timzone info returned by localtime()
+ // on BSD and Linux, and global variables altzone/timezone on SunOS.
+
+ // NOTE: at the moment of passing the DST adjustment (twice a year)
+ // the local time value returned by now() may be incorrect.
+ // the application should call tzupdate() from time to time if it
+ // is supposed to be running infinitely, e.g. if it's a daemon.
+
+ // always rely on UTC time inside your application whenever possible.
+ timeval tv;
+ gettimeofday(&tv, nil);
+ int edays = tv.tv_sec / 86400 // days since Unix "Epoch", i.e. 01/01/1970
+ + 719162; // plus days between 01/01/0001 and Unix Epoch
+ int esecs = tv.tv_sec % 86400; // the remainder, i.e. seconds since midnight
+ datetime res = mkdt(edays, esecs * 1000 + tv.tv_usec / 1000);
+
+ if (!utc)
+ res += tzoffset() * 60 * 1000;
+ return res;
+#endif
+}
+
+
+void ptdecl tzupdate()
+{
+ tzset();
+}
+
+
+int ptdecl tzoffset()
+{
+#if defined(WIN32)
+ TIME_ZONE_INFORMATION tz;
+ DWORD res = GetTimeZoneInformation(&tz);
+ if ((res == TIME_ZONE_ID_DAYLIGHT) && (tz.DaylightDate.wMonth != 0))
+ return - (tz.Bias + tz.DaylightBias);
+ else
+ return - tz.Bias;
+
+#else // UNIX
+ time_t t0 = time(0);
+
+#if defined(__sun__)
+#ifdef PTYPES_ST
+ // localtime_r() is not available without -D_REENTRANT
+ tm* t = localtime(&t0);
+ if(t->tm_isdst != 0 && daylight != 0)
+#else
+ tm t;
+ localtime_r(&t0, &t);
+ if(t.tm_isdst != 0 && daylight != 0)
+#endif
+ return - altzone / 60;
+ else
+ return - timezone / 60;
+
+#elif defined(__CYGWIN__)
+ time_t local_time = time(NULL);
+ tm gt;
+ gmtime_r(&local_time, >);
+ time_t gmt_time = mktime(>);
+ return (local_time - gmt_time) / 60;
+
+#elif defined(__hpux)
+ tm local;
+ localtime_r(&t0, &local);
+ local.tm_isdst = 0;
+ time_t t1 = mktime(&local);
+ return - (timezone - (t1 - t0)) / 60;
+
+#else // other UNIX
+ tm t;
+ localtime_r(&t0, &t);
+ return t.tm_gmtoff / 60;
+#endif
+
+#endif
+}
+
+
+datetime ptdecl utodatetime(time_t u)
+{
+ return _unixepoch + large(u) * 1000;
+}
+
+
+}
diff --git a/source/ptypes/ptime.h b/source/ptypes/ptime.h
@@ -0,0 +1,72 @@
+/*
+ *
+ * C++ Portable Types Library (PTypes)
+ * Version 2.1.1 Released 27-Jun-2007
+ *
+ * Copyright (C) 2001-2007 Hovik Melikyan
+ *
+ * http://www.melikyan.com/ptypes/
+ *
+ */
+
+#ifndef __PTIME_H__
+#define __PTIME_H__
+
+#ifndef __PPORT_H__
+#include "pport.h"
+#endif
+
+#ifndef __PTYPES_H__
+#include "ptypes.h"
+#endif
+
+
+#include <time.h>
+
+
+namespace ptypes {
+
+// datetime type: 64-bit, number of milliseconds since midnight 01/01/0001
+typedef large datetime;
+
+#define invdatetime LLCONST(-1)
+
+#define _msecsmax 86400000 // number of milliseconds in one day
+#define _daysmax 3652059 // number of days between 01/01/0001 and 12/31/9999
+#define _datetimemax LLCONST(315537897600000) // max. allowed number for datetime type
+#define _unixepoch LLCONST(62135596800000) // difference between time_t and datetime in milliseconds
+
+
+// datetime general utilities
+inline int days(datetime d) { return int(d / _msecsmax); }
+inline int msecs(datetime d) { return int(d % _msecsmax); }
+
+ptpublic datetime ptdecl mkdt(int days, int msecs);
+ptpublic bool ptdecl isvalid(datetime);
+ptpublic datetime ptdecl now(bool utc = true);
+ptpublic void ptdecl tzupdate();
+ptpublic int ptdecl tzoffset();
+ptpublic string ptdecl dttostring(datetime, const char* fmt);
+ptpublic string ptdecl nowstring(const char* fmt, bool utc = true);
+ptpublic datetime ptdecl utodatetime(time_t u);
+ptpublic struct tm* ptdecl dttotm(datetime dt, struct tm& t);
+
+// date/calendar manipulation
+ptpublic bool ptdecl isleapyear(int year);
+ptpublic int ptdecl daysinmonth(int year, int month);
+ptpublic int ptdecl daysinyear(int year, int month);
+ptpublic int ptdecl dayofweek(datetime);
+ptpublic bool ptdecl isdatevalid(int year, int month, int day);
+ptpublic datetime ptdecl encodedate(int year, int month, int day);
+ptpublic bool ptdecl decodedate(datetime, int& year, int& month, int& day);
+
+// time manipulation
+ptpublic bool ptdecl istimevalid(int hour, int min, int sec, int msec = 0);
+ptpublic datetime ptdecl encodetime(int hour, int min, int sec, int msec = 0);
+ptpublic bool ptdecl decodetime(datetime, int& hour, int& min, int& sec, int& msec);
+ptpublic bool ptdecl decodetime(datetime, int& hour, int& min, int& sec);
+
+
+}
+
+#endif // __PTIME_H__
diff --git a/source/ptypes/ptimedsem.cxx b/source/ptypes/ptimedsem.cxx
@@ -0,0 +1,130 @@
+/*
+ *
+ * C++ Portable Types Library (PTypes)
+ * Version 2.1.1 Released 27-Jun-2007
+ *
+ * Copyright (C) 2001-2007 Hovik Melikyan
+ *
+ * http://www.melikyan.com/ptypes/
+ *
+ */
+
+#ifdef WIN32
+# include <windows.h>
+#else
+# include <sys/time.h>
+# include <pthread.h>
+# include <errno.h>
+#endif
+
+#include "pasync.h"
+
+
+namespace ptypes {
+
+
+static void tsem_fail()
+{
+ fatal(CRIT_FIRST + 41, "Timed semaphore failed");
+}
+
+
+#ifdef WIN32
+
+
+timedsem::timedsem(int initvalue)
+{
+ handle = CreateSemaphore(nil, initvalue, 65535, nil);
+ if (handle == 0)
+ tsem_fail();
+}
+
+
+timedsem::~timedsem()
+{
+ CloseHandle(handle);
+}
+
+
+bool timedsem::wait(int timeout)
+{
+ uint r = WaitForSingleObject(handle, timeout);
+ if (r == WAIT_FAILED)
+ tsem_fail();
+ return r != WAIT_TIMEOUT;
+}
+
+
+void timedsem::post()
+{
+ if (ReleaseSemaphore(handle, 1, nil) == 0)
+ tsem_fail();
+}
+
+
+#else
+
+
+inline void tsem_syscheck(int r)
+{
+ if (r != 0)
+ tsem_fail();
+}
+
+
+timedsem::timedsem(int initvalue)
+ : unknown(), count(initvalue)
+{
+ tsem_syscheck(pthread_mutex_init(&mtx, 0));
+ tsem_syscheck(pthread_cond_init(&cond, 0));
+}
+
+
+timedsem::~timedsem()
+{
+ pthread_cond_destroy(&cond);
+ pthread_mutex_destroy(&mtx);
+}
+
+
+bool timedsem::wait(int timeout)
+{
+ pthread_mutex_lock(&mtx);
+ while (count <= 0)
+ {
+ if (timeout >= 0)
+ {
+ timespec abs_ts;
+ timeval cur_tv;
+ gettimeofday(&cur_tv, NULL);
+ abs_ts.tv_sec = cur_tv.tv_sec + timeout / 1000;
+ abs_ts.tv_nsec = cur_tv.tv_usec * 1000
+ + (timeout % 1000) * 1000000;
+ int rc = pthread_cond_timedwait(&cond, &mtx, &abs_ts);
+ if (rc == ETIMEDOUT) {
+ pthread_mutex_unlock(&mtx);
+ return false;
+ }
+ }
+ else
+ pthread_cond_wait(&cond, &mtx);
+ }
+ count--;
+ pthread_mutex_unlock(&mtx);
+ return true;
+}
+
+
+void timedsem::post()
+{
+ pthread_mutex_lock(&mtx);
+ count++;
+ pthread_cond_signal(&cond);
+ pthread_mutex_unlock(&mtx);
+}
+
+
+#endif
+
+
+}
diff --git a/source/ptypes/ptrigger.cxx b/source/ptypes/ptrigger.cxx
@@ -0,0 +1,101 @@
+/*
+ *
+ * C++ Portable Types Library (PTypes)
+ * Version 2.1.1 Released 27-Jun-2007
+ *
+ * Copyright (C) 2001-2007 Hovik Melikyan
+ *
+ * http://www.melikyan.com/ptypes/
+ *
+ */
+
+#ifdef WIN32
+# include <windows.h>
+#else
+# include <sys/time.h>
+# include <pthread.h>
+# include <errno.h>
+#endif
+
+#include "pasync.h"
+
+
+namespace ptypes {
+
+
+static void trig_fail()
+{
+ fatal(CRIT_FIRST + 41, "Trigger failed");
+}
+
+
+
+#ifdef WIN32
+
+
+trigger::trigger(bool autoreset, bool state)
+{
+ handle = CreateEvent(0, !autoreset, state, 0);
+ if (handle == 0)
+ trig_fail();
+}
+
+
+#else
+
+
+inline void trig_syscheck(int r)
+{
+ if (r != 0)
+ trig_fail();
+}
+
+
+trigger::trigger(bool iautoreset, bool istate)
+ : state(int(istate)), autoreset(iautoreset)
+{
+ trig_syscheck(pthread_mutex_init(&mtx, 0));
+ trig_syscheck(pthread_cond_init(&cond, 0));
+}
+
+
+trigger::~trigger()
+{
+ pthread_cond_destroy(&cond);
+ pthread_mutex_destroy(&mtx);
+}
+
+
+void trigger::wait()
+{
+ pthread_mutex_lock(&mtx);
+ while (state == 0)
+ pthread_cond_wait(&cond, &mtx);
+ if (autoreset)
+ state = 0;
+ pthread_mutex_unlock(&mtx);
+}
+
+
+void trigger::post()
+{
+ pthread_mutex_lock(&mtx);
+ state = 1;
+ if (autoreset)
+ pthread_cond_signal(&cond);
+ else
+ pthread_cond_broadcast(&cond);
+ pthread_mutex_unlock(&mtx);
+}
+
+
+void trigger::reset()
+{
+ state = 0;
+}
+
+
+#endif
+
+
+}
diff --git a/source/ptypes/ptypes.h b/source/ptypes/ptypes.h
@@ -0,0 +1,1159 @@
+/*
+ *
+ * C++ Portable Types Library (PTypes)
+ * Version 2.1.1 Released 27-Jun-2007
+ *
+ * Copyright (C) 2001-2007 Hovik Melikyan
+ *
+ * http://www.melikyan.com/ptypes/
+ *
+ */
+
+#ifndef __PTYPES_H__
+#define __PTYPES_H__
+
+
+#ifndef __PPORT_H__
+#include "pport.h"
+#endif
+
+#include <string.h>
+
+
+namespace ptypes {
+
+
+#ifdef _MSC_VER
+#pragma pack(push, 4)
+// disable "non dll-interface class '...' used as base for dll-interface class '...'" warning
+#pragma warning(disable : 4275)
+// disable "conditional expression constant" warning
+#pragma warning(push)
+#pragma warning(disable : 4127)
+#endif
+
+
+ptpublic int __PFASTCALL pincrement(int* target);
+ptpublic int __PFASTCALL pdecrement(int* target);
+ptpublic int __PFASTCALL pexchange(int* target, int value);
+ptpublic void* __PFASTCALL pexchange(void** target, void* value);
+
+template <class T> inline T* tpexchange(T** target, T* value)
+ { return (T*)pexchange((void**)target, (void*)value); }
+
+
+#if ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 3)) || (__GNUC__ == 4) || defined(__hpux)
+# define VARIANT_TYPECAST_HACK
+#endif
+
+
+// -------------------------------------------------------------------- //
+// --- string class --------------------------------------------------- //
+// -------------------------------------------------------------------- //
+
+// dynamic string class with thread-safe ref-counted buffer
+
+struct _strrec
+{
+ int refcount;
+ int length;
+#ifdef __EMSCRIPTEN__
+ int _padding0;
+ int _padding1;
+#endif
+};
+typedef _strrec* _pstrrec;
+
+
+#define STR_BASE(x) (_pstrrec(x)-1)
+#define STR_REFCOUNT(x) (STR_BASE(x)->refcount)
+#define STR_LENGTH(x) (STR_BASE(x)->length)
+
+#define PTR_TO_PSTRING(p) (pstring(&(p)))
+#define PTR_TO_STRING(p) (*PTR_TO_PSTRING(p))
+
+
+ptpublic extern char* emptystr;
+
+class ptpublic variant;
+
+
+class ptpublic string
+{
+ friend class variant;
+
+protected:
+ char* data;
+
+ static void idxerror();
+
+ void _alloc(int);
+ void _realloc(int);
+ void _free();
+
+ void initialize() { data = emptystr; }
+ void initialize(const char*, int);
+ void initialize(const char*);
+ void initialize(char);
+ void initialize(const string& s);
+ void initialize(const char*, int, const char*, int);
+ void initialize(const variant&);
+ void finalize();
+
+ void assign(const char*, int);
+ void assign(const char*);
+ void assign(const string&);
+ void assign(char);
+
+#ifdef CHECK_BOUNDS
+ void idx(int index) const { if (unsigned(index) >= unsigned(STR_LENGTH(data))) idxerror(); }
+#else
+ void idx(int) const { }
+#endif
+
+ string(const char* s1, int len1, const char* s2, int len2) { initialize(s1, len1, s2, len2); }
+
+public:
+ friend int length(const string& s);
+ friend int refcount(const string& s);
+ friend void assign(string& s, const char* buf, int len);
+ friend void clear(string& s);
+ friend bool isempty(const string& s);
+ ptpublic friend char* ptdecl setlength(string&, int);
+ ptpublic friend char* ptdecl unique(string&);
+ ptpublic friend void ptdecl concat(string& s, const char* sc, int catlen);
+ ptpublic friend void ptdecl concat(string& s, const char* s1);
+ ptpublic friend void ptdecl concat(string& s, char s1);
+ ptpublic friend void ptdecl concat(string& s, const string& s1);
+ ptpublic friend string ptdecl copy(const string& s, int from, int cnt);
+ ptpublic friend string ptdecl copy(const string& s, int from);
+ ptpublic friend void ptdecl ins(const char* s1, int s1len, string& s, int at);
+ ptpublic friend void ptdecl ins(const char* s1, string& s, int at);
+ ptpublic friend void ptdecl ins(char s1, string& s, int at);
+ ptpublic friend void ptdecl ins(const string& s1, string& s, int at);
+ ptpublic friend void ptdecl del(string& s, int at, int cnt);
+ ptpublic friend void ptdecl del(string& s, int at);
+ ptpublic friend int ptdecl pos(const char* s1, const string& s);
+ ptpublic friend int ptdecl pos(char s1, const string& s);
+ friend int pos(const string& s1, const string& s);
+ ptpublic friend int ptdecl rpos(char s1, const string& s);
+ ptpublic friend bool ptdecl contains(const char* s1, int len, const string& s, int at);
+ ptpublic friend bool ptdecl contains(const char* s1, const string& s, int at);
+ ptpublic friend bool ptdecl contains(char s1, const string& s, int at);
+ ptpublic friend bool ptdecl contains(const string& s1, const string& s, int at);
+ ptpublic friend string ptdecl dup(const string& s);
+
+ string() { initialize(); }
+ string(const char* sc, int initlen) { initialize(sc, initlen); }
+ string(const char* sc) { initialize(sc); }
+ string(char c) { initialize(c); }
+ string(const string& s) { initialize(s); }
+ ~string() { finalize(); }
+
+#ifdef VARIANT_TYPECAST_HACK
+ string(const variant& v) { initialize(v); }
+#endif
+
+ string& operator= (const char* sc) { assign(sc); return *this; }
+ string& operator= (char c) { assign(c); return *this; }
+ string& operator= (const string& s) { assign(s); return *this; }
+ string& operator+= (const char* sc) { concat(*this, sc); return *this; }
+ string& operator+= (char c) { concat(*this, c); return *this; }
+ string& operator+= (const string& s) { concat(*this, s); return *this; }
+
+ string operator+ (const char* sc) const;
+ string operator+ (char c) const;
+ string operator+ (const string& s) const;
+
+ ptpublic friend string ptdecl operator+ (const char* sc, const string& s);
+ ptpublic friend string ptdecl operator+ (char c, const string& s);
+
+ bool operator== (const char* sc) const { return strcmp(data, sc) == 0; }
+ bool operator== (char) const;
+ bool operator== (const string&) const;
+ bool operator!= (const char* sc) const { return !(*this == sc); }
+ bool operator!= (char c) const { return !(*this == c); }
+ bool operator!= (const string& s) const { return !(*this == s); }
+
+ friend bool operator== (const char*, const string&);
+ friend bool operator== (char, const string&);
+ friend bool operator!= (const char*, const string&);
+ friend bool operator!= (char, const string&);
+
+ operator const char*() const { return data; }
+ operator const uchar*() const { return (uchar*)data; }
+
+ char& operator[] (int i) { idx(i); return unique(*this)[i]; }
+ const char& operator[] (int i) const { idx(i); return data[i]; }
+
+ friend void initialize(string& s);
+ friend void initialize(string& s, const string& s1);
+ friend void initialize(string& s, const char* s1);
+ friend void finalize(string& s);
+};
+
+
+typedef string* pstring;
+
+inline int length(const string& s) { return STR_LENGTH(s.data); }
+inline int refcount(const string& s) { return STR_REFCOUNT(s.data); }
+inline void assign(string& s, const char* buf, int len) { s.assign(buf, len); }
+inline void clear(string& s) { s.finalize(); }
+inline bool isempty(const string& s) { return length(s) == 0; }
+inline int pos(const string& s1, const string& s) { return pos(s1.data, s); }
+inline bool operator== (const char* sc, const string& s){ return s == sc; }
+inline bool operator== (char c, const string& s) { return s == c; }
+inline bool operator!= (const char* sc, const string& s){ return s != sc; }
+inline bool operator!= (char c, const string& s) { return s != c; }
+inline void initialize(string& s) { s.initialize(); }
+inline void initialize(string& s, const string& s1) { s.initialize(s1); }
+inline void initialize(string& s, const char* s1) { s.initialize(s1); }
+inline void finalize(string& s) { s.finalize(); }
+
+
+ptpublic extern int stralloc;
+
+ptpublic extern string nullstring;
+
+
+// -------------------------------------------------------------------- //
+// --- string utilities ----------------------------------------------- //
+// -------------------------------------------------------------------- //
+
+
+ptpublic string ptdecl fill(int width, char pad);
+ptpublic string ptdecl pad(const string& s, int width, char c, bool left = true);
+
+ptpublic string ptdecl itostring(large value, int base, int width = 0, char pad = 0);
+ptpublic string ptdecl itostring(ularge value, int base, int width = 0, char pad = 0);
+ptpublic string ptdecl itostring(int value, int base, int width = 0, char pad = 0);
+ptpublic string ptdecl itostring(unsigned value, int base, int width = 0, char pad = 0);
+ptpublic string ptdecl itostring(large v);
+ptpublic string ptdecl itostring(ularge v);
+ptpublic string ptdecl itostring(int v);
+ptpublic string ptdecl itostring(unsigned v);
+
+ptpublic large ptdecl stringtoi(const char*);
+ptpublic large ptdecl stringtoie(const char*);
+ptpublic ularge ptdecl stringtoue(const char*, int base);
+
+ptpublic string ptdecl lowercase(const char* s);
+ptpublic string ptdecl lowercase(const string& s);
+
+char hex4(char c);
+
+inline char locase(char c)
+ { if (c >= 'A' && c <= 'Z') return char(c + 32); return c; }
+
+inline char upcase(char c)
+ { if (c >= 'a' && c <= 'z') return char(c - 32); return c; }
+
+inline int hstrlen(const char* p) // some Unix systems do not accept NULL
+ { return p == nil ? 0 : (int)strlen(p); }
+
+
+
+
+// -------------------------------------------------------------------- //
+// --- character set -------------------------------------------------- //
+// -------------------------------------------------------------------- //
+
+
+const int _csetbits = 256;
+const int _csetbytes = _csetbits / 8;
+const int _csetwords = _csetbytes / sizeof(int);
+const char _csetesc = '~';
+
+
+class ptpublic cset
+{
+protected:
+ char data[_csetbytes];
+
+ void assign(const cset& s) { memcpy(data, s.data, _csetbytes); }
+ void assign(const char* setinit);
+ void clear() { memset(data, 0, _csetbytes); }
+ void fill() { memset(data, -1, _csetbytes); }
+ void include(char b) { data[uchar(b) / 8] |= uchar(1 << (uchar(b) % 8)); }
+ void include(char min, char max);
+ void exclude(char b) { data[uchar(b) / 8] &= uchar(~(1 << (uchar(b) % 8))); }
+ void unite(const cset& s);
+ void subtract(const cset& s);
+ void intersect(const cset& s);
+ void invert();
+ bool contains(char b) const { return (data[uchar(b) / 8] & (1 << (uchar(b) % 8))) != 0; }
+ bool eq(const cset& s) const { return memcmp(data, s.data, _csetbytes) == 0; }
+ bool le(const cset& s) const;
+
+public:
+ cset() { clear(); }
+ cset(const cset& s) { assign(s); }
+ cset(const char* setinit) { assign(setinit); }
+
+ cset& operator= (const cset& s) { assign(s); return *this; }
+ cset& operator+= (const cset& s) { unite(s); return *this; }
+ cset& operator+= (char b) { include(b); return *this; }
+ cset operator+ (const cset& s) const { cset t = *this; return t += s; }
+ cset operator+ (char b) const { cset t = *this; return t += b; }
+ cset& operator-= (const cset& s) { subtract(s); return *this; }
+ cset& operator-= (char b) { exclude(b); return *this; }
+ cset operator- (const cset& s) const { cset t = *this; return t -= s; }
+ cset operator- (char b) const { cset t = *this; return t -= b; }
+ cset& operator*= (const cset& s) { intersect(s); return *this; }
+ cset operator* (const cset& s) const { cset t = *this; return t *= s; }
+ cset operator! () const { cset t = *this; t.invert(); return t; }
+ bool operator== (const cset& s) const { return eq(s); }
+ bool operator!= (const cset& s) const { return !eq(s); }
+ bool operator<= (const cset& s) const { return le(s); }
+ bool operator>= (const cset& s) const { return s.le(*this); }
+
+ friend cset operator+ (char b, const cset& s);
+ friend bool operator& (char b, const cset& s);
+ friend void assign(cset& s, const char* setinit);
+ friend void clear(cset& s);
+ friend void fill(cset& s);
+ friend void include(cset& s, char b);
+ friend void include(cset& s, char min, char max);
+ friend void exclude(cset& s, char b);
+
+ ptpublic friend string ptdecl asstring(const cset& s);
+};
+
+
+inline cset operator+ (char b, const cset& s) { return s + b; }
+inline bool operator& (char b, const cset& s) { return s.contains(b); }
+inline void assign(cset& s, const char* setinit) { s.assign(setinit); }
+inline void clear(cset& s) { s.clear(); }
+inline void fill(cset& s) { s.fill(); }
+inline void include(cset& s, char b) { s.include(b); }
+inline void include(cset& s, char min, char max) { s.include(min, max); }
+inline void exclude(cset& s, char b) { s.exclude(b); }
+
+
+// -------------------------------------------------------------------- //
+// --- basic abstract classes ----------------------------------------- //
+// -------------------------------------------------------------------- //
+
+// basic class with virtual destructor; historically was used as a base
+// for all list items. also helps to count the number of created and
+// destroyed objects in a program (objalloc global) in DEBUG mode, to
+// detect memory leaks. most classes in ptypes are derived from unknown.
+
+ptpublic extern int objalloc;
+
+class ptpublic unknown
+{
+private:
+ // make all classes non-copyable by default
+ unknown(const unknown&);
+ const unknown& operator= (const unknown&);
+public:
+#ifdef COUNT_OBJALLOC
+ unknown() { pincrement(&objalloc); }
+ virtual ~unknown() { pdecrement(&objalloc); }
+#else
+ unknown() { }
+ virtual ~unknown() { }
+#endif
+};
+
+typedef unknown* punknown;
+
+
+// provide non-copyable base for all classes that are
+// not derived from 'unknown'
+
+class ptpublic noncopyable
+{
+private:
+ noncopyable(const noncopyable&);
+ const noncopyable& operator= (const noncopyable&);
+public:
+ noncopyable() {}
+ ~noncopyable() {}
+};
+
+
+
+// -------------------------------------------------------------------- //
+// --- exception ------------------------------------------------------ //
+// -------------------------------------------------------------------- //
+
+// the basic exception class. NOTE: the library always throws dynamically
+// allocated exception objects.
+
+class ptpublic exception: public unknown
+{
+protected:
+ string message;
+public:
+ exception(const char* imsg);
+ exception(const string& imsg);
+ virtual ~exception();
+ virtual const string& get_message() const { return message; }
+};
+
+
+// conversion exception class for stringtoie() and stringtoue()
+
+class ptpublic econv: public exception
+{
+public:
+ econv(const char* msg): exception(msg) {}
+ econv(const string& msg): exception(msg) {}
+ virtual ~econv();
+};
+
+
+// -------------------------------------------------------------------- //
+// --- tpodlist ------------------------------------------------------- //
+// -------------------------------------------------------------------- //
+
+// _podlist implements dynamic array of small POD structures; it serves
+// as a basis for all list types in the library. this class is undocumented.
+// tpodlist template must be used instead.
+
+class ptpublic _podlist: public noncopyable
+{
+protected:
+ void* list; // pointer to the array
+ int count; // number of items in the list
+ int capacity; // allocated for the list
+ int itemsize; // list item size
+
+ static void idxerror();
+
+ _podlist& operator =(const _podlist& t);
+
+ void grow();
+ void* doins(int index);
+ void doins(int index, const _podlist&);
+ void* doget(int index) const { return (char*)list + index * itemsize; }
+ void dodel(int index);
+ void dodel(int index, int count);
+ void dopop();
+
+#ifdef CHECK_BOUNDS
+ void idx(int index) const { if (unsigned(index) >= unsigned(count)) idxerror(); }
+ void idxa(int index) const { if (unsigned(index) > unsigned(count)) idxerror(); }
+#else
+ void idx(int) const { }
+ void idxa(int) const { }
+#endif
+
+public:
+ _podlist(int itemsize);
+ ~_podlist();
+
+ int get_count() const { return count; }
+ void set_count(int newcount, bool zero = false);
+ int get_capacity() const { return capacity; }
+ void set_capacity(int newcap);
+ void clear() { set_count(0); }
+ void pack() { set_capacity(count); }
+ void* ins(int index) { idxa(index); return doins(index); }
+ void ins(int index, const _podlist& t) { idxa(index); doins(index, t); }
+ void* add();
+ void add(const _podlist& t);
+ void* operator [](int index) { idx(index); return doget(index); }
+ void* top() { return operator [](count - 1); }
+ void del(int index) { idx(index); dodel(index); }
+ void del(int index, int count) { idx(index); dodel(index, count); }
+ void pop() { idx(0); dopop(); }
+};
+
+
+// tpodlist is a fully-inlined template based on _podlist
+
+template <class X, bool initzero = false> class tpodlist: public _podlist
+{
+protected:
+ X& dozero(X& t) { if (initzero) memset(&t, 0, sizeof(X)); return t; }
+ X& doget(int index) const { return ((X*)list)[index]; }
+ X& doins(int index) { X& t = *(X*)_podlist::doins(index); return dozero(t); }
+ void doins(int index, const X& item) { *(X*)_podlist::doins(index) = item; }
+
+public:
+ tpodlist(): _podlist(sizeof(X)) {}
+ tpodlist<X, initzero>& operator =(const tpodlist<X, initzero>& t)
+ { _podlist::operator =(t); return *this; }
+
+ void set_count(int newcount) { _podlist::set_count(newcount, initzero); }
+ X& ins(int index) { idxa(index); return doins(index); }
+ void ins(int index, const X& item) { idxa(index); doins(index, item); }
+ void ins(int index, const tpodlist<X, initzero>& t)
+ { _podlist::ins(index, t); }
+ X& add() { grow(); return dozero(doget(count++)); }
+ void add(const X& item) { grow(); doget(count++) = item; }
+ void add(const tpodlist<X, initzero>& t)
+ { _podlist::add(t); }
+ X& operator [](int index) { idx(index); return doget(index); }
+ const X& operator [](int index) const { idx(index); return doget(index); }
+ X& top() { idx(0); return doget(count - 1); }
+};
+
+
+// -------------------------------------------------------------------- //
+// --- tobjlist ------------------------------------------------------- //
+// -------------------------------------------------------------------- //
+
+// _objlist is a base for the tobjlist template, don't use it directly.
+// also, _objlist is a base for _strlist and derivatives.
+
+class ptpublic _objlist: public unknown, protected tpodlist<void*, true>
+{
+protected:
+ struct
+ {
+ unsigned ownobjects :1; // list is responsible for destroying the items; used in _objlist
+ unsigned ownslobjects :1; // same but for _strlist items (in _stritem structure)
+ unsigned sorted :1; // sorted list (_objlist+)
+ unsigned duplicates :1; // sorted: allows duplicate keys (_objlist+)
+ unsigned casesens :1; // sorted: string comparison is case sensitive (_strlist+)
+ unsigned _reserved :27;
+ } config;
+
+ _objlist(bool ownobjects); // we hide this ctor, since _objlist actually can't free objects
+
+ void* doget(int index) const { return ((void**)list)[index]; }
+ void doput(int index, void* obj);
+ void dodel(int index);
+ void dodel(int index, int count);
+ void* dopop();
+ void dofree(int index, int count);
+
+ virtual void dofree(void* obj); // pure method; defined in tobjlist instances
+ virtual int compare(const void* key, const void* obj) const; // pure method; defined in _strlist
+
+public:
+ _objlist();
+ virtual ~_objlist();
+
+ int get_count() const { return count; }
+ void set_count(int newcount);
+ int get_capacity() const { return capacity; }
+ void set_capacity(int newcap) { tpodlist<void*,true>::set_capacity(newcap); }
+ void clear() { set_count(0); }
+ void pack() { tpodlist<void*,true>::pack(); }
+ void ins(int index, void* obj) { tpodlist<void*,true>::ins(index, obj); }
+ void add(void* obj) { tpodlist<void*,true>::add(obj); }
+ void put(int index, void* obj) { idx(index); doput(index, obj); }
+ void* operator [](int index) const { idx(index); return doget(index); }
+ void* top() const { idx(0); return doget(count - 1); }
+ void* pop() { idx(0); return dopop(); }
+ void del(int index) { idx(index); dodel(index); }
+ void del(int index, int count) { idx(index); dodel(index, count); }
+ int indexof(void* obj) const;
+ bool search(const void* key, int& index) const;
+};
+
+
+// the tobjlist template implements a list of pointers to arbitrary
+// structures. optionally can automatically free objects (ownobjects)
+// when removed from a list. only 2 virtual functions are being
+// instantiated by this template, the rest is static code in _objlist.
+
+template <class X> class tobjlist: public _objlist
+{
+protected:
+ X* doget(int index) const { return (X*)_objlist::doget(index); }
+ virtual void dofree(void* obj);
+
+public:
+ tobjlist(bool ownobjects = false): _objlist(ownobjects) {}
+ virtual ~tobjlist();
+
+ bool get_ownobjects() const { return config.ownobjects; }
+ void set_ownobjects(bool newval) { config.ownobjects = newval; }
+ void ins(int index, X* obj) { _objlist::ins(index, obj); }
+ void add(X* obj) { _objlist::add(obj); }
+ void put(int index, X* obj) { _objlist::put(index, obj); }
+ X* operator [](int index) const { idx(index); return (X*)doget(index); }
+ X* top() const { return (X*)_objlist::top(); }
+ X* pop() { return (X*)_objlist::pop(); }
+ int indexof(X* obj) const { return _objlist::indexof(obj); }
+
+#ifdef PTYPES19_COMPAT
+ friend inline void ins(tobjlist& s, int i, X* obj) { s.ins(i, obj); }
+ friend inline int add(tobjlist& s, X* obj) { s.add(obj); return s.get_count() - 1; }
+ friend inline void put(tobjlist& s, int i, X* obj) { s.put(i, obj); }
+ friend inline int indexof(const tobjlist& s, X* obj) { return s.indexof(obj); }
+ friend inline int push(tobjlist& s, X* obj) { s.add(obj); return s.get_count() - 1; }
+ friend inline X* pop(tobjlist& s) { return (X*)s.pop(); }
+ friend inline X* top(const tobjlist& s) { return (X*)s.top(); }
+ friend inline X* get(const tobjlist& s, int i) { return (X*)s[i]; }
+#endif
+};
+
+
+template <class X> void tobjlist<X>::dofree(void* item)
+{
+ delete (X*)item;
+}
+
+
+template <class X> tobjlist<X>::~tobjlist()
+{
+ set_count(0);
+}
+
+
+// -------------------------------------------------------------------- //
+// --- tstrlist ------------------------------------------------------- //
+// -------------------------------------------------------------------- //
+
+// _strlist is a base for the tstrlist template
+
+
+typedef int slflags; // left for compatibility
+
+#define SL_SORTED 1
+#define SL_DUPLICATES 2
+#define SL_CASESENS 4
+#define SL_OWNOBJECTS 8
+
+
+struct _stritem
+{
+ string key;
+ void* obj;
+
+ _stritem(const string& ikey, void* iobj)
+ : key(ikey), obj(iobj) {}
+};
+
+
+class ptpublic _strlist: protected tobjlist<_stritem>
+{
+protected:
+ static void sortederror();
+ static void notsortederror();
+ static void duperror();
+
+ virtual void dofree(void* item);
+ virtual int compare(const void* key, const void* item) const;
+ virtual void dofreeobj(void* obj); // pure; tstrlist overrides it
+
+ const string& dogetkey(int index) const { return doget(index)->key; }
+ void* dogetobj(int index) const { return doget(index)->obj; }
+ void doins(int index, const string& key, void* obj);
+ void doput(int index, const string& key, void* obj);
+ void doput(int index, void* obj);
+
+public:
+ _strlist(int flags = 0);
+ virtual ~_strlist();
+
+ int get_count() const { return count; }
+ void set_count(int newcount) { tobjlist<_stritem>::set_count(newcount); }
+ int get_capacity() const { return capacity; }
+ void set_capacity(int newcap) { tobjlist<_stritem>::set_capacity(newcap); }
+ void clear() { tobjlist<_stritem>::clear(); }
+ void pack() { tobjlist<_stritem>::pack(); }
+ bool get_sorted() const { return config.sorted; }
+ bool get_duplicates() const { return config.duplicates; }
+ bool get_casesens() const { return config.casesens; }
+ bool get_ownobjects() const { return config.ownslobjects; }
+ void set_ownobjects(bool newval) { config.ownslobjects = newval; }
+ void ins(int index, const string& key, void* obj) { idxa(index); doins(index, key, obj); }
+ void put(int index, const string& key, void* obj) { idx(index); doput(index, key, obj); }
+ void put(int index, void* obj) { idx(index); doput(index, obj); }
+ int put(const string& key, void* obj);
+ int add(const string& key, void* obj);
+ void* operator [](int index) const { idx(index); return dogetobj(index); }
+ void* operator [](const char* key) const;
+ const string& getkey(int index) const { idx(index); return dogetkey(index); }
+ bool search(const char* key, int& index) const { return _objlist::search(key, index); }
+ void del(int index) { idx(index); dodel(index); }
+ void del(int index, int delcount) { idx(index); dodel(index, delcount); }
+ void del(const char* key) { put(key, nil); }
+ int indexof(const char* key) const;
+ int indexof(void* obj) const;
+};
+
+
+// the tstrlist template implements a list of string/object pairs,
+// optionally sorted for fast searching by string key.
+
+template <class X> class tstrlist: public _strlist
+{
+protected:
+ virtual void dofreeobj(void* obj);
+
+public:
+ tstrlist(int flags = 0): _strlist(flags) {}
+ virtual ~tstrlist();
+
+ void ins(int index, const string& key, X* obj) { _strlist::ins(index, key, obj); }
+ void put(int index, const string& key, X* obj) { _strlist::put(index, key, obj); }
+ void put(int index, X* obj) { _strlist::put(index, obj); }
+ int put(const string& key, X* obj) { return _strlist::put(key, obj); }
+ int add(const string& key, X* obj) { return _strlist::add(key, obj); }
+ X* operator [](int index) const { return (X*)_strlist::operator [](index); }
+ X* operator [](const char* key) const { return (X*)_strlist::operator [](key); }
+ int indexof(X* obj) const { return _strlist::indexof(obj); }
+ int indexof(const char* key) const { return _strlist::indexof(key); }
+
+#ifdef PTYPES19_COMPAT
+ // pre-2.0 interface for backwards compatibility
+ friend inline void ins(tstrlist& s, int i, const string& str, X* obj) { s.ins(i, str, obj); }
+ friend inline int add(tstrlist& s, const string& str, X* obj) { return s.add(str, obj); }
+ friend inline void put(tstrlist& s, int i, const string& str, X* obj) { s.put(i, str, obj); }
+ friend inline void put(tstrlist& s, int i, X* obj) { s.put(i, obj); }
+ friend inline int indexof(const tstrlist& s, X* obj) { return s.indexof(obj); }
+ friend inline X* get(const tstrlist& s, int i) { return (X*)s[i]; }
+#endif
+};
+
+
+template <class X> void tstrlist<X>::dofreeobj(void* obj)
+{
+ delete (X*)obj;
+}
+
+
+template <class X> tstrlist<X>::~tstrlist()
+{
+ set_count(0);
+}
+
+
+// -------------------------------------------------------------------- //
+// --- textmap -------------------------------------------------------- //
+// -------------------------------------------------------------------- //
+
+// textmap is a list of string pairs (key/value)
+
+struct _textitem
+{
+ string key;
+ string value;
+
+ _textitem(const string& ikey, const string& ivalue)
+ : key(ikey), value(ivalue) {}
+};
+
+
+class ptpublic textmap: protected tobjlist<_textitem>
+{
+protected:
+ virtual int compare(const void* key, const void* item) const;
+ const string& dogetvalue(int index) const { return doget(index)->value; }
+ const string& dogetkey(int index) const { return doget(index)->key; }
+
+public:
+ textmap(bool casesens = false);
+ virtual ~textmap();
+
+ int get_count() const { return tobjlist<_textitem>::get_count(); }
+ void pack() { tobjlist<_textitem>::pack(); }
+ void clear() { tobjlist<_textitem>::clear(); }
+ int put(const string& key, const string& value);
+ void del(int index) { idx(index); dodel(index); }
+ void del(const char* key) { put(key, nullstring); }
+ const string& get(int index) const { idx(index); return dogetvalue(index); }
+ const string& getkey(int index) const { idx(index); return dogetkey(index); }
+ const string& get(const char* key) const;
+ const string& operator [](int index) const { return get(index); }
+ const string& operator [](const char* key) const { return get(key); }
+ int indexof(const char* key) const;
+};
+
+
+// -------------------------------------------------------------------- //
+// --- component ------------------------------------------------------ //
+// -------------------------------------------------------------------- //
+
+// the component class is an abstract class that provides reference
+// counting and delete notification mechanisms. all stream classes
+// in ptypes are derived from component.
+
+// class ID's for all basic types: the first byte (least significant)
+// contains the base ID, the next is for the second level of inheritance,
+// etc. total of 4 levels allowed for basic types. call classid() for an
+// object, mask out first N bytes of interest and compare with a CLASS_XXX
+// value. f.ex. to determine whether an object is of type infile or any
+// derivative: (o->classid() & 0xffff) == CLASS2_INFILE. this scheme is for
+// internal use by PTypes and Objection; partly replaces the costly C++ RTTI
+// system.
+
+// first level of inheritance
+const int CLASS_UNDEFINED = 0x00000000;
+const int CLASS_INSTM = 0x00000001;
+const int CLASS_OUTSTM = 0x00000002;
+const int CLASS_UNIT = 0x00000003;
+
+// second level of inheritance
+const int CLASS2_INFILE = 0x00000100 | CLASS_INSTM;
+const int CLASS2_INMEMORY = 0x00000200 | CLASS_INSTM;
+const int CLASS2_FDX = 0x00000300 | CLASS_INSTM;
+const int CLASS2_OUTFILE = 0x00000100 | CLASS_OUTSTM;
+const int CLASS2_OUTMEMORY = 0x00000200 | CLASS_OUTSTM;
+
+// third level of inheritance
+const int CLASS3_LOGFILE = 0x00010000 | CLASS2_OUTFILE;
+const int CLASS3_IPSTM = 0x00020000 | CLASS2_FDX;
+const int CLASS3_NPIPE = 0x00030000 | CLASS2_FDX;
+
+
+class ptpublic component: public unknown
+{
+protected:
+ int refcount; // reference counting, used by addref() and release()
+ tobjlist<component>* freelist; // list of components to notify about destruction, safer alternative to ref-counting
+ void* typeinfo; // reserved for future use
+
+ virtual void freenotify(component* sender);
+
+public:
+ component();
+ virtual ~component();
+ void addnotification(component* obj);
+ void delnotification(component* obj);
+
+ ptpublic friend component* ptdecl addref(component*);
+ ptpublic friend bool ptdecl release(component*);
+ friend int refcount(component* c);
+
+ virtual int classid();
+
+ void set_typeinfo(void* t) { typeinfo = t; }
+ void* get_typeinfo() { return typeinfo; }
+};
+
+typedef component* pcomponent;
+
+
+inline int refcount(component* c) { return c->refcount; }
+
+component* ptdecl addref(component* c );
+
+template <class T> inline T* taddref(T* c)
+ { return (T*)addref((component*)c); }
+
+
+template <class T> class compref
+{
+protected:
+ T* ref;
+public:
+ compref() { ref = 0; }
+ compref(const compref<T>& r) { ref = taddref<T>(r.ref); }
+ compref(T* c) { ref = taddref<T>(c); }
+ ~compref() { release(ref); }
+ compref<T>& operator =(T* c);
+ compref<T>& operator =(const compref<T>& r) { return operator =(r.ref); }
+ T& operator *() const { return *ref; }
+ T* operator ->() const { return ref; }
+ bool operator ==(const compref<T>& r) const { return ref == r.ref; }
+ bool operator ==(T* c) const { return ref == c; }
+ bool operator !=(const compref<T>& r) const { return ref != r.ref; }
+ bool operator !=(T* c) const { return ref != c; }
+ operator T*() const { return ref; }
+};
+
+
+template <class T> compref<T>& compref<T>::operator =(T* c)
+{
+ release(tpexchange<T>(&ref, taddref<T>(c)));
+ return *this;
+}
+
+
+// -------------------------------------------------------------------- //
+// --- variant -------------------------------------------------------- //
+// -------------------------------------------------------------------- //
+
+
+enum {
+ VAR_NULL,
+ VAR_INT,
+ VAR_BOOL,
+ VAR_FLOAT,
+ VAR_STRING,
+ VAR_ARRAY,
+ VAR_OBJECT,
+
+ VAR_COMPOUND = VAR_STRING
+};
+
+
+class ptpublic _varray;
+
+
+class ptpublic variant
+{
+ friend class string;
+ friend class _varray;
+
+protected:
+ int tag; // VAR_XXX
+ union {
+ large i; // 64-bit int value
+ bool b; // bool value
+ double f; // double value
+ char* s; // string object; can't declare as string because of the union
+ _varray* a; // pointer to a reference-counted _strlist object
+ component* o; // pointer to a reference-counted component object (or derivative)
+ } value; // we need this name to be able to copy the entire union in some situations
+
+ void initialize() { tag = VAR_NULL; }
+ void initialize(large v) { tag = VAR_INT; value.i = v; }
+ void initialize(bool v) { tag = VAR_BOOL; value.b = v; }
+ void initialize(double v) { tag = VAR_FLOAT; value.f = v; }
+ void initialize(const char* v) { tag = VAR_STRING; ptypes::initialize(PTR_TO_STRING(value.s), v); }
+ void initialize(const string& v) { tag = VAR_STRING; ptypes::initialize(PTR_TO_STRING(value.s), v); }
+ void initialize(_varray* a);
+ void initialize(component* o);
+ void initialize(const variant& v);
+ void finalize();
+
+ void assign(large);
+ void assign(bool);
+ void assign(double);
+ void assign(const char*);
+ void assign(const string&);
+ void assign(_varray*);
+ void assign(component*);
+ void assign(const variant&);
+
+ bool equal(const variant& v) const;
+
+ variant(_varray* a) { initialize(a); }
+
+public:
+ // construction
+ variant() { initialize(); }
+ variant(int v) { initialize(large(v)); }
+ variant(unsigned int v) { initialize(large(v)); }
+ variant(large v) { initialize(v); }
+ variant(bool v) { initialize(v); }
+ variant(double v) { initialize(v); }
+ variant(const char* v) { initialize(v); }
+ variant(const string& v) { initialize(v); }
+ variant(component* v) { initialize(v); }
+ variant(const variant& v) { initialize(v); }
+ ~variant() { finalize(); }
+
+ // assignment
+ variant& operator= (int v) { assign(large(v)); return *this; }
+ variant& operator= (unsigned int v) { assign(large(v)); return *this; }
+ variant& operator= (large v) { assign(v); return *this; }
+ variant& operator= (bool v) { assign(v); return *this; }
+ variant& operator= (double v) { assign(v); return *this; }
+ variant& operator= (const char* v) { assign(v); return *this; }
+ variant& operator= (const string& v) { assign(v); return *this; }
+ variant& operator= (component* v) { assign(v); return *this; }
+ variant& operator= (const variant& v) { assign(v); return *this; }
+
+ // typecast
+ operator int() const;
+ operator unsigned int() const;
+ operator long() const;
+ operator unsigned long() const;
+ operator large() const;
+ operator bool() const;
+ operator double() const;
+ operator string() const;
+ operator component*() const;
+
+ // comparison
+ bool operator== (const variant& v) const { return equal(v); }
+ bool operator!= (const variant& v) const { return !equal(v); }
+
+ // typification
+ ptpublic friend void ptdecl clear(variant&);
+ friend int vartype(const variant& v);
+ friend bool isnull(const variant& v);
+ friend bool isint(const variant& v);
+ friend bool isbool(const variant& v);
+ friend bool isfloat(const variant& v);
+ friend bool isstring(const variant& v);
+ friend bool isarray(const variant& v);
+ friend bool isobject(const variant& v);
+ friend bool iscompound(const variant& v);
+
+ // array manipulation
+ ptpublic friend void ptdecl aclear(variant&);
+ ptpublic friend variant ptdecl aclone(const variant&);
+ ptpublic friend const variant& ptdecl get(const variant&, const string& key);
+ ptpublic friend const variant& ptdecl get(const variant&, large key);
+ ptpublic friend void ptdecl put(variant&, const string& key, const variant& item);
+ ptpublic friend void ptdecl put(variant&, large key, const variant& item);
+ ptpublic friend void ptdecl del(variant&, const string& key);
+ ptpublic friend void ptdecl del(variant&, large key);
+
+ // indexed access to arrays
+ ptpublic friend int ptdecl alength(const variant&);
+ ptpublic friend void ptdecl apack(variant&);
+ ptpublic friend bool ptdecl anext(const variant& a, int&, variant& item);
+ ptpublic friend bool ptdecl anext(const variant& a, int&, variant& item, string& key);
+ ptpublic friend int ptdecl aadd(variant&, const variant& item);
+ ptpublic friend void ptdecl aput(variant&, int index, const variant& item);
+ ptpublic friend void ptdecl ains(variant&, int index, const variant& item);
+ ptpublic friend void ptdecl adel(variant&, int index);
+ ptpublic friend const variant& ptdecl aget(const variant&, int index);
+ ptpublic friend string ptdecl akey(const variant&, int index);
+
+ const variant& operator[](const char* key) const { return get(*this, string(key)); }
+ const variant& operator[](const string& key) const { return get(*this, key); }
+ const variant& operator[](large key) const { return get(*this, key); }
+
+ // 'manual' initialization/finalization, undocumented. use with care!
+ friend void initialize(variant& v);
+ friend void initialize(variant& v, large i);
+ friend void initialize(variant& v, int i);
+ friend void initialize(variant& v, unsigned int i);
+ friend void initialize(variant& v, bool i);
+ friend void initialize(variant& v, double i);
+ friend void initialize(variant& v, const char* i);
+ friend void initialize(variant& v, const string& i);
+ friend void initialize(variant& v, component* i);
+ friend void initialize(variant& v, const variant& i);
+ friend void finalize(variant& v);
+};
+
+
+typedef variant* pvariant;
+
+
+inline int vartype(const variant& v) { return v.tag; }
+inline bool isnull(const variant& v) { return v.tag == VAR_NULL; }
+inline bool isint(const variant& v) { return v.tag == VAR_INT; }
+inline bool isbool(const variant& v) { return v.tag == VAR_BOOL; }
+inline bool isfloat(const variant& v) { return v.tag == VAR_FLOAT; }
+inline bool isstring(const variant& v) { return v.tag == VAR_STRING; }
+inline bool isarray(const variant& v) { return v.tag == VAR_ARRAY; }
+inline bool isobject(const variant& v) { return v.tag == VAR_OBJECT; }
+inline bool iscompound(const variant& v) { return v.tag >= VAR_COMPOUND; }
+
+inline void initialize(variant& v) { v.initialize(); }
+inline void initialize(variant& v, large i) { v.initialize(i); }
+inline void initialize(variant& v, int i) { v.initialize(large(i)); }
+inline void initialize(variant& v, unsigned int i) { v.initialize(large(i)); }
+inline void initialize(variant& v, bool i) { v.initialize(i); }
+inline void initialize(variant& v, double i) { v.initialize(i); }
+inline void initialize(variant& v, const char* i) { v.initialize(i); }
+inline void initialize(variant& v, const string& i) { v.initialize(i); }
+inline void initialize(variant& v, component* i) { v.initialize(i); }
+inline void initialize(variant& v, const variant& i) { v.initialize(i); }
+inline void finalize(variant& v) { if (v.tag >= VAR_COMPOUND) v.finalize(); }
+
+
+ptpublic extern const variant nullvar;
+
+
+// variant exception class; may be thrown when a variant
+// is being typecast'ed to 32-bit int and the value is
+// out of range
+
+class ptpublic evariant: public exception
+{
+protected:
+public:
+ evariant(const char* msg): exception(msg) {}
+ evariant(const string& msg): exception(msg) {}
+ virtual ~evariant();
+};
+
+
+
+// -------------------------------------------------------------------- //
+// --- pre-2.0 compatibility declarations ----------------------------- //
+// -------------------------------------------------------------------- //
+
+
+#ifdef PTYPES19_COMPAT
+
+// ptypes-1.9 objlist and strlist: accept only 'unknown' and
+// derivatives as a base type
+
+class ptpublic objlist: public tobjlist<unknown>
+{
+public:
+ objlist(bool ownobjects = false);
+ virtual ~objlist();
+};
+
+inline int length(const _objlist& s) { return s.get_count(); }
+inline void setlength(_objlist& s, int newcount) { s.set_count(newcount); }
+inline void pack(_objlist& s) { s.pack(); }
+inline void clear(_objlist& s) { s.clear(); }
+inline int push(_objlist& s, unknown* obj) { s.add(obj); return length(s) - 1; }
+inline unknown* top(const _objlist& s) { return (unknown*)s.top(); }
+inline void ins(_objlist& s, int i, unknown* obj) { s.ins(i, obj); }
+inline int add(_objlist& s, unknown* obj) { s.add(obj); return length(s) - 1; }
+inline void put(_objlist& s, int i, unknown* obj) { s.put(i, obj); }
+inline unknown* get(const _objlist& s, int i) { return (unknown*)s[i]; }
+inline unknown* pop(_objlist& s) { return (unknown*)s.pop(); }
+inline void del(_objlist& s, int i) { s.del(i); }
+inline int indexof(const _objlist& s, unknown* obj) { return s.indexof(obj); }
+
+
+class ptpublic strlist: public tstrlist<unknown>
+{
+public:
+ strlist(int flags = 0);
+ virtual ~strlist();
+};
+
+inline int length(const _strlist& s) { return s.get_count(); }
+inline void clear(_strlist& s) { s.clear(); }
+inline void pack(_strlist& s) { s.pack(); }
+inline bool search(const _strlist& s, const char* key, int& i) { return s.search(key, i); }
+inline void ins(_strlist& s, int i, const string& key, unknown* obj) { s.ins(i, key, obj); }
+inline int add(_strlist& s, const string& key, unknown* obj) { return s.add(key, obj); }
+inline void put(_strlist& s, int i, const string& key, unknown* obj) { s.put(i, key, obj); }
+inline void put(_strlist& s, int i, unknown* obj) { s.put(i, obj); }
+inline unknown* get(const _strlist& s, int i) { return (unknown*)s[i]; }
+inline const string& getstr(const _strlist& s, int i) { return s.getkey(i); }
+inline void del(_strlist& s, int i) { s.del(i); }
+inline int find(const _strlist& s, const char* key) { return s.indexof(key); }
+inline int indexof(const _strlist& s, unknown* obj) { return s.indexof(obj); }
+
+
+// ptypes-1.9 strmap: now replaced with _strlist(SL_SORTED)
+
+class ptpublic strmap: public tstrlist<unknown>
+{
+public:
+ strmap(int flags = 0);
+ virtual ~strmap();
+};
+
+inline void put(strmap& m, const string& key, unknown* obj) { m.put(key, obj); }
+inline unknown* get(const strmap& m, const char* key) { return m[key]; }
+inline void del(strmap& m, const char* key) { m.del(key); }
+
+template <class X> class tstrmap: public strmap
+{
+public:
+ tstrmap(): strmap() {}
+ tstrmap(int iflags): strmap(iflags) {}
+ friend inline X* get(const tstrmap& m, const char* str) { return (X*)ptypes::get((const strmap&)m, str); }
+ friend inline void put(tstrmap& m, const string& str, X* obj) { unknown* t = obj; ptypes::put(m, str, t); }
+ X* operator[] (const char* str) const { return (X*)ptypes::get(*this, str); }
+};
+
+
+// ptypes-1.9 textmap interface
+
+inline int length(const textmap& m) { return m.get_count(); }
+inline void clear(textmap& m) { m.clear(); }
+inline const string& get(const textmap& m, const string& k) { return m.get(k); }
+inline void put(textmap& m, const string& k, const string& v) { m.put(k, v); }
+inline void del(textmap& m, const string& k) { m.del(k); }
+
+
+#endif // PTYPES19_COMPAT
+
+
+#ifdef _MSC_VER
+#pragma warning(pop)
+#pragma pack(pop)
+#endif
+
+
+}
+
+#endif // __PTYPES_H__
diff --git a/source/ptypes/ptypes_test.cxx b/source/ptypes/ptypes_test.cxx
@@ -0,0 +1,1622 @@
+/*
+ *
+ * C++ Portable Types Library (PTypes)
+ * Version 2.1.1 Released 27-Jun-2007
+ *
+ * Copyright (C) 2001-2007 Hovik Melikyan
+ *
+ * http://www.melikyan.com/ptypes/
+ *
+ */
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "pport.h"
+#include "ptypes.h"
+#include "pstreams.h"
+#include "pinet.h"
+#include "ptime.h"
+
+
+#ifndef PTYPES_ST
+#include "pasync.h"
+#endif
+
+
+USING_PTYPES
+
+
+void passert(bool cond)
+{
+ if (!cond)
+ {
+ fatal(0xa3, "*** ASSERTION FAILED ***");
+ }
+}
+
+
+void showstr(const char* shouldbe, const char* is)
+{
+ passert(strcmp(shouldbe, is) == 0);
+ pout.putf("[%s] %s\n", shouldbe, is);
+}
+
+
+void showstr(const char* shouldbe, const string& is)
+{
+ showstr(shouldbe, pconst(is));
+}
+
+
+void showhex(const char* shouldbe, const char* is, int islen)
+{
+ string s;
+ int i;
+ for (i = 0; i < islen; i++)
+ s += itostring((unsigned char)is[i], 16, 2);
+ s = lowercase(s);
+ showstr(shouldbe, s);
+}
+
+
+void showint(int shouldbe, int is)
+{
+ passert(shouldbe == is);
+ pout.putf("[%d] %d\n", shouldbe, is);
+}
+
+
+void showint(large shouldbe, large is)
+{
+ passert(shouldbe == is);
+ pout.putf("[%lld] %lld\n", shouldbe, is);
+}
+
+
+void showchar(char shouldbe, char is)
+{
+ passert(shouldbe == is);
+ pout.putf("[%c] %c\n", shouldbe, is);
+}
+
+
+//
+// string test
+//
+
+
+void const_string_call(const string& s)
+{
+ showchar('R', s[2]);
+}
+
+
+void string_test1()
+{
+ pout.put("\n--- STRING CLASS\n");
+
+ static char strbuf[10] = "STRING";
+
+ char c = 'A';
+
+ string s1 = "string";
+ s1 = s1;
+ s1 += s1;
+ del(s1, 6, 6);
+ string s2 = s1;
+ string s3;
+ string s4(strbuf, strlen(strbuf));
+ string s5 = 'A';
+ string s6 = c;
+
+ showstr("string", s1);
+ showstr(s1, s2);
+ showstr("", s3);
+ showstr("STRING", s4);
+ const_string_call(s4);
+ showchar('I', s4[3]);
+ showint(6, length(s4));
+ showint(2, refcount(s1));
+ clear(s2);
+ showint(1, refcount(s1));
+ s2 = s1;
+ unique(s1);
+ showint(1, refcount(s1));
+ setlength(s1, 64);
+ string s7 = s1;
+ setlength(s1, 3);
+ showstr("str", s1);
+ showstr("strING", s1 += copy(s4, 3, 3));
+ del(s1, 3, 3);
+ showstr("str", s1);
+ ins("ing", s1, 0);
+ showstr("ingstr", s1);
+ s2 = "str" + s1 + "ing";
+ showstr("stringstring", s2);
+ s3 = s2;
+ s3 = "strungstrung";
+ s3 = s2;
+ s3 = "strung";
+ del(s3, 4);
+ showstr("stru", s3);
+
+ s2 = "str" + s1;
+ s2 = s1 + "str";
+ s2 += "str";
+
+ s2 = 's' + s1;
+ s2 = s1 + 's';
+ s2 += 's';
+
+ s2 = c + s1;
+ s2 = s1 + c;
+ s2 += c;
+
+ s2 = 'a';
+ s2 = c;
+}
+
+
+void string_test2()
+{
+ pout.put("\n--- STRING CLASS\n");
+
+ string s1 = "ingstr";
+ string s2 = "stringstring";
+ string s4 = "STRING";
+
+ showint(1, pos('t', s2));
+ showint(2, pos("ri", s2));
+ showint(3, pos(s1, s2));
+ showint(1, contains("tr", s2, 1));
+ showint(0, contains("tr", s2, 2));
+ showint(1, s4 == "STRING");
+ showint(1, "STRING" == s4);
+ showchar('R', s4[2]);
+
+ string s5 = 'A';
+ showint(1, s5 == 'A');
+ showint(1, 'A' == s5);
+
+ showstr("123456789", itostring(123456789));
+ showstr("123", itostring(char(123)));
+ showstr("-000123", itostring(-123, 10, 7, '0'));
+ showstr("0ABCDE", itostring(0xabcde, 16, 6));
+ showstr("-9223372036854775808", itostring(LARGE_MIN));
+ showstr("18446744073709551615", itostring(ULARGE_MAX));
+
+ showint(1234, (int)stringtoi("1234"));
+ showint(large(0x15AF), stringtoue("15AF", 16));
+ showint(LARGE_MAX, stringtoue("5zzzzzzzzzz", 64));
+
+ try
+ {
+ // out of range by 1
+ stringtoue("18446744073709551616", 10);
+ fatal(0xb0, "Conversion overflow not detected");
+ }
+ catch (econv* e)
+ {
+ showstr("Out of range: '18446744073709551616'", e->get_message());
+ delete e;
+ }
+
+ showint(large(123), stringtoie("123"));
+ showint(large(-123), stringtoie("-123"));
+ showint(LARGE_MIN, stringtoie("-9223372036854775808"));
+ showint(LARGE_MAX, stringtoie("9223372036854775807"));
+
+ try
+ {
+ // out of range by 1
+ stringtoie("9223372036854775808");
+ fatal(0xb0, "Conversion overflow not detected");
+ }
+ catch (econv* e)
+ {
+ showstr("Out of range: '9223372036854775808'", e->get_message());
+ delete e;
+ }
+
+ showstr("abcabc", lowercase(s1 = "aBCAbc"));
+ showstr("abcabc", lowercase(s1));
+}
+
+
+void string_benchmarks()
+{
+ pout.put("\n--- STRING BENCHMARKS\n");
+
+ int i;
+ string s1 = "string";
+ string s2;
+
+ // for the first time we run the test to let the VM settle down and stop swapping
+ for (i = 0; i < 156000; i++)
+ s2 += s1;
+
+ // here is the actual test
+ clear(s2);
+ datetime start = now();
+ for (i = 0; i < 156000; i++)
+ s2 += s1;
+ datetime diff = now() - start;
+
+ pout.putf("Performance index compared to WinNT on P3/800MHz (smaller = better):\n%.3f\n", diff / 1000.0);
+}
+
+
+//
+// cset test
+//
+
+void cset_test()
+{
+ pout.put("\n--- CSET CLASS\n");
+
+ cset s1 = "~09~0a~0d~F0 A-Z~~";
+ cset s2 = "A-F";
+ char c = 'B';
+
+ showstr("~09~0a~0d A-Z~~~f0", asstring(s1));
+ s1 -= char(0xf0);
+ s1 -= cset("~00-~20");
+ showstr("A-Z~~", asstring(s1));
+ s1 -= s2;
+ showstr("G-Z~~", asstring(s1));
+ s1 += s2;
+ s1 += ' ';
+ showstr(" A-Z~~", asstring(s1));
+ showint(1, s2 == cset("A-F"));
+ showint(1, s2 <= s1);
+ s1 -= 'A';
+ s1 -= c;
+ showint(0, s2 <= s1);
+ s1 = s1 + char(0xf1);
+ showint(1, char(0xf1) & s1);
+ showint(0, char(0xf2) & s1);
+}
+
+
+//
+// podlist
+//
+
+
+void const_podlist_test(const tpodlist<int, true>& p)
+{
+// int& i = p[0];
+ showint(7, p[1]);
+}
+
+
+void podlist_test()
+{
+ pout.put("\n--- PODLIST\n");
+
+ tpodlist<int, true> p;
+ p.add() = 6;
+ p.add(8);
+ p.ins(1, 7);
+ showint(7, p[1]);
+ const_podlist_test(p);
+ showint(3, p.get_count());
+ p.set_count(5);
+ p.ins(5) = 10;
+ showint(10, p.top());
+ p.pop();
+
+ tpodlist<int, true> p1;
+ p1.add(p);
+ p1.pop();
+ p1.add(p);
+}
+
+
+//
+// ptrlist
+//
+
+
+struct known: public unknown
+{
+ int value;
+ known(int ivalue): value(ivalue) {}
+};
+
+typedef tobjlist<known> knownlist;
+
+
+string ol_asstring(const knownlist& s)
+{
+ string ret = "{";
+ for (int i = 0; i < s.get_count(); i++) {
+ if (i > 0)
+ concat(ret, ", ");
+ ret += itostring(s[i]->value);
+ }
+ concat(ret, '}');
+ return ret;
+}
+
+
+void ptrlist_test()
+{
+ pout.put("\n--- PTRLIST\n");
+
+ knownlist s1(true);
+ known* obj;
+
+ s1.add(new known(10));
+ s1.ins(0, new known(5));
+ s1.ins(2, obj = new known(20));
+ s1.add(new known(30));
+ s1.add(new known(40));
+ s1.put(4, new known(45));
+ s1.del(4);
+ s1.del(1);
+
+ s1[0];
+ showint(3, s1.get_count());
+ showint(1, s1.indexof(obj));
+ showstr("{5, 20, 30}", ol_asstring(s1));
+
+ s1.clear();
+ showstr("{}", ol_asstring(s1));
+}
+
+
+
+//
+// strlist
+//
+
+
+typedef tstrlist<known> knownstrlist;
+
+
+string sl_asstring(const knownstrlist& s)
+{
+ string ret = "{";
+ for (int i = 0; i < s.get_count(); i++)
+ {
+ if (i > 0)
+ concat(ret, ", ");
+ ret += s.getkey(i) + ":" + itostring(s[i]->value);
+ }
+ concat(ret, '}');
+ return ret;
+}
+
+
+void strlist_test()
+{
+ pout.put("\n--- STRLIST\n");
+
+ knownstrlist s1(SL_OWNOBJECTS);
+ known* obj;
+
+ s1.add("ten", new known(10));
+ s1.ins(0, "five", new known(5));
+ s1.ins(2, "twenty", obj = new known(20));
+ s1.add("thirty", new known(30));
+ s1.add("forty", new known(40));
+ s1.put(4, "forty five", new known(45));
+ s1.del(4);
+ s1.del(1);
+
+ showint(3, s1.get_count());
+ showint(1, s1.indexof(obj));
+ showint(2, s1.indexof("thirty"));
+ showint(2, s1.indexof("THIRTY"));
+ showint(-1, s1.indexof("forty"));
+
+ showstr("{five:5, twenty:20, thirty:30}", sl_asstring(s1));
+
+ knownstrlist s2(SL_OWNOBJECTS | SL_SORTED | SL_CASESENS);
+ s2.add("five", new known(5));
+ s2.add("ten", new known(10));
+ s2.add("twenty", new known(20));
+ s2.add("thirty", new known(30));
+ s2.add("forty", new known(40));
+
+ showint(5, s2.get_count());
+ showint(3, s2.indexof("thirty"));
+ showint(-1, s2.indexof("THIRTY"));
+ showint(-1, s2.indexof("hovik"));
+
+ showstr("{five:5, forty:40, ten:10, thirty:30, twenty:20}", sl_asstring(s2));
+
+ s2.clear();
+ showstr("{}", sl_asstring(s2));
+
+ tstrlist<known> s3(SL_OWNOBJECTS | SL_SORTED | SL_DUPLICATES);
+
+ s3.add("a", nil);
+ s3.add("b", nil);
+ s3.add("b", nil);
+ s3.add("b", nil);
+ s3.add("b", nil);
+ s3.add("b", nil);
+ s3.add("b", nil);
+ s3.add("c", nil);
+
+ showint(1, s3.indexof("b"));
+ s3.del(1, 2);
+
+ tstrlist<known> s(SL_OWNOBJECTS | SL_SORTED);
+
+ s.put("five", new known(5));
+ s.put("ten", new known(10));
+ s.put("twenty", new known(20));
+ s.put("thirty", new known(30));
+ s.put("forty", new known(40));
+
+ showint(20, s["twenty"]->value);
+ showint(0, pintptr(s["hovik"]));
+ showint(5, s.get_count());
+ s.put("twenty", nil);
+ showint(4, s.get_count());
+}
+
+
+//
+// textmap
+//
+
+
+void textmap_test()
+{
+ pout.put("\n--- TEXTMAP CLASS\n");
+
+ textmap c;
+
+ c.put("name1", "value1");
+ c.put("name2", "value2");
+ c.put("name1", "value3");
+ showstr("name2", c.getkey(1));
+ c.put("name2", "");
+ showint(1, c.get_count());
+ showstr("value3", c["name1"]);
+ showstr("", c["name2"]);
+}
+
+
+
+//
+// streams
+//
+
+
+char buf1[] = "This is a test.";
+char buf2[] = "The file should contain readable text.";
+
+const char* fname = "stmtest.txt";
+
+void outfile_test()
+{
+ pout.put("\n--- OUTFILE CLASS\n");
+
+ showint(8, sizeof(off_t));
+
+ outfile f(fname, false);
+ f.set_umode(0600);
+ f.set_bufsize(3);
+
+ f.open();
+ f.put(buf1[0]);
+ f.put(buf1[1]);
+ f.put("is is a TEST.");
+ f.seek(-5, IO_END);
+ f.put("tes*/");
+ f.seek(13);
+ f.put("t.");
+ f.puteol();
+ f.close();
+
+ f.set_append(true);
+ f.open();
+ f.write(buf2, strlen(buf2));
+ f.puteol();
+ f.close();
+
+ pnull.put("This should go to nowhere I");
+ pnull.put("This should go to nowhere II");
+}
+
+
+void infile_test()
+{
+ pout.put("\n--- INFILE CLASS\n");
+
+ compref<instm> f = &pin;
+ f = new infile(fname);
+ f->set_bufsize(3);
+
+ char temp[4];
+
+ f->open();
+ pout.putf("%c", f->get());
+ pout.putf("%s\n", pconst(f->line()));
+ f->read(temp, sizeof temp - 1);
+ temp[sizeof temp - 1] = 0;
+ pout.putf("%s", temp);
+ f->get();
+ f->putback();
+ pout.putf("%s", pconst(f->token(cset("~20-~FF"))));
+ f->preview();
+ if (f->get_eol())
+ {
+ f->skipline();
+ pout.put("\n");
+ }
+ if (f->get_eof())
+ pout.put("EOF\n");
+// f.error(1, "Test error message");
+
+}
+
+
+void mem_test()
+{
+ pout.put("\n--- OUT/IN MEMORY CLASS\n");
+
+ {
+ outmemory m(12);
+ m.open();
+ m.put("MEMOry");
+ m.put(" c");
+ m.put("lass is working");
+ m.seek(1);
+ m.put("emo");
+ showstr("Memory class", m.get_strdata());
+ // try reuse
+ m.open();
+ m.put("memory");
+ showstr("memory", m.get_strdata());
+ }
+ {
+ inmemory m("");
+ m.open();
+ showstr("", m.token("*"));
+ m.set_strdata("gArbaGe");
+ m.open();
+ // try reuse
+ m.set_strdata("string strong");
+ m.set_bufsize(2); // has no effect
+ m.open();
+ showstr("string", m.token("a-z"));
+ m.seek(-6, IO_END);
+ showstr("strong", m.token("a-z"));
+ }
+}
+
+
+#ifndef PTYPES_ST
+
+//
+// multithreading
+//
+
+//
+// rwlock test
+//
+
+const int rw_max_threads = 30;
+const int rw_max_tries = 30;
+const int rw_max_delay = 20;
+const int rw_rw_ratio = 5;
+const bool rw_swap = false;
+
+
+class rwthread: public thread
+{
+protected:
+ virtual void execute();
+public:
+ rwthread(): thread(false) {}
+ virtual ~rwthread() { waitfor(); }
+};
+
+
+rwlock rw;
+
+int reader_cnt = 0;
+int writer_cnt = 0;
+int total_writers = 0;
+int total_readers = 0;
+int max_readers = 0;
+
+
+int prand(int max)
+{
+ return rand() % max;
+}
+
+
+void rwthread::execute()
+{
+
+ for(int i = 0; i < rw_max_tries; i++)
+ {
+ psleep(prand(rw_max_delay));
+ bool writer = prand(rw_rw_ratio) == 0;
+ if (writer ^ rw_swap)
+ {
+ rw.wrlock();
+ pout.put('w');
+ if (pincrement(&writer_cnt) > 1)
+ fatal(0xa0, "Writer: Huh?! Writers in here?");
+ pincrement(&total_writers);
+ }
+ else
+ {
+ rw.rdlock();
+ pout.put('.');
+ int t;
+ if ((t = pincrement(&reader_cnt)) > max_readers)
+ max_readers = t;
+ if (writer_cnt > 0)
+ fatal(0xa1, "Reader: Huh?! Writers in here?");
+ pincrement(&total_readers);
+ }
+ psleep(prand(rw_max_delay));
+ if (writer ^ rw_swap)
+ pdecrement(&writer_cnt);
+ else
+ pdecrement(&reader_cnt);
+ rw.unlock();
+ }
+}
+
+
+
+void rwlock_test()
+{
+// #ifdef __PTYPES_RWLOCK__
+ pout.put("\n--- RWLOCK\n");
+
+ rwthread* threads[rw_max_threads];
+
+ srand((unsigned)time(0));
+
+ int i;
+ for(i = 0; i < rw_max_threads; i++)
+ {
+ threads[i] = new rwthread();
+ threads[i]->start();
+ }
+ for(i = 0; i < rw_max_threads; i++)
+ delete threads[i];
+
+ pout.putf("\nmax readers: %d\n", max_readers);
+ pout.putline("do writers 'starve'?");
+// #endif
+}
+
+
+//
+// jobqueue test ----------------------------------------------------------
+//
+
+
+const int MSG_MYJOB = MSG_USER + 1;
+const int NUM_JOB_THREADS = 3;
+
+
+class jobthread: public thread
+{
+protected:
+ int id;
+ jobqueue* jq;
+ virtual void execute();
+public:
+ jobthread(int iid, jobqueue* ijq): thread(false), id(iid), jq(ijq) {}
+ ~jobthread() { waitfor(); }
+};
+
+
+void jobthread::execute()
+{
+ bool quit = false;
+ while (!quit)
+ {
+ message* m = jq->getmessage();
+ try
+ {
+ switch (m->id)
+ {
+ case MSG_MYJOB:
+ // ... do the job ...
+ psleep(prand(10));
+ // report
+ pout.putf("Thread %d finished the job (param=%d)\n", id, m->param);
+ break;
+ case MSG_QUIT:
+ quit = true;
+ break;
+ }
+ }
+ catch(...)
+ {
+ // the message object must be freed!
+ delete m;
+ throw;
+ }
+ delete m;
+ }
+}
+
+
+void jobqueue_test()
+{
+ pout.put("\n--- JOBQUEUE\n");
+
+ jobqueue jq(3);
+ tobjlist<jobthread> threads(true);
+
+ srand((unsigned)time(0));
+
+ // create the thread pool and start all threads
+ int i;
+ for(i = 0; i < NUM_JOB_THREADS; i++)
+ {
+ jobthread* j = new jobthread(i + 1, &jq);
+ j->start();
+ threads.add(j);
+ }
+
+ // post jobs for processing
+ jq.post(MSG_MYJOB, 1);
+ jq.post(MSG_MYJOB, 2);
+ jq.post(MSG_MYJOB, 3);
+ jq.post(MSG_MYJOB, 4);
+ jq.post(MSG_MYJOB, 5);
+ jq.post(MSG_MYJOB, 6);
+ jq.post(MSG_MYJOB, 7);
+ jq.post(MSG_MYJOB, 8);
+
+ // terminate all threads
+ for(i = 0; i < NUM_JOB_THREADS; i++)
+ jq.post(MSG_QUIT);
+
+ // threads are being waitfor()'ed and destroyed
+ // automatically by the list object
+}
+
+
+
+//
+// msgqueue test ----------------------------------------------------------
+//
+
+
+const int MSG_DIAG = MSG_USER + 1;
+
+
+//
+// msgqueue test
+//
+
+//
+// class diagmessage
+//
+
+class diagmessage: public message
+{
+protected:
+ string module;
+ string diagstr;
+ friend class diagthread;
+public:
+ diagmessage(string imodule, string idiagstr)
+ : message(MSG_DIAG), module(imodule),
+ diagstr(idiagstr) {}
+};
+
+
+//
+// class diagthread
+//
+
+class diagthread: public thread, protected msgqueue
+{
+protected:
+ virtual void execute(); // override thread::execute()
+ virtual void cleanup(); // override thread::cleanup()
+ virtual void msghandler(message& msg); // override msgqueue::msghandler()
+public:
+ diagthread(): thread(false), msgqueue() { }
+ void postdiag(string module, string diagstr);
+ void postquit();
+};
+
+
+void diagthread::postdiag(string module, string diagstr)
+{
+ msgqueue::post(new diagmessage(module, diagstr));
+}
+
+
+void diagthread::postquit()
+{
+ msgqueue::post(MSG_QUIT);
+}
+
+
+void diagthread::execute()
+{
+ // starts message queue processing; calls
+ // msghandler for each message
+ msgqueue::run();
+}
+
+
+void diagthread::cleanup()
+{
+}
+
+
+void diagthread::msghandler(message& msg)
+{
+ switch (msg.id)
+ {
+ case MSG_DIAG:
+ {
+ diagmessage& m = (diagmessage&)msg;
+ pout.putf("%s: %s\n", pconst(m.module), pconst(m.diagstr));
+ }
+ break;
+ default:
+ defhandler(msg);
+ }
+}
+
+
+//
+// class testthread
+//
+
+
+class testthread: public thread
+{
+protected:
+ diagthread* diag;
+ string myname;
+ virtual void execute();
+ virtual void cleanup();
+public:
+ semaphore sem;
+ timedsem tsem;
+ testthread(diagthread* idiag)
+ : thread(false), diag(idiag), myname("testthread"), sem(0), tsem(0) {}
+};
+
+
+void testthread::execute()
+{
+ diag->postdiag(myname, "starts and enters sleep for 1 second");
+ psleep(1000);
+ diag->postdiag(myname, "signals the timed semaphore");
+ tsem.post();
+ diag->postdiag(myname, "releases the simple semaphore");
+ sem.post();
+ diag->postdiag(myname, "enters sleep for 1 more second");
+ psleep(1000);
+}
+
+
+void testthread::cleanup()
+{
+ diag->postdiag(myname, "terminates");
+}
+
+
+int thread_test()
+{
+ pout.put("\n--- THREAD AND SEMAPHORE CLASSES\n");
+
+ int v = 0;
+ showint(0, pexchange(&v, 5));
+ showint(5, pexchange(&v, 10));
+
+ void* pv = 0;
+ passert(pexchange(&pv, &v) == 0);
+ passert(pexchange(&pv, 0) == &v);
+
+ showint(11, pincrement(&v));
+ showint(10, pdecrement(&v));
+
+ diagthread diag;
+ testthread thr(&diag);
+
+ string myname = "main";
+
+ diag.start();
+ thr.start();
+
+ diag.postdiag(myname, "waits 5 secs for the timed semaphore (actually wakes up after a second)");
+ thr.tsem.wait(5000); // must exit after 1 second instead of 5
+
+ diag.postdiag(myname, "waits for the semaphore");
+ thr.sem.wait();
+ diag.postdiag(myname, "now waits for testthread to terminate");
+ thr.waitfor();
+
+ diag.postquit();
+ diag.waitfor();
+ return 0;
+}
+
+
+//
+// trigger test
+//
+
+
+class trigthread: public thread
+{
+protected:
+ diagthread* diag;
+ string myname;
+ virtual void execute();
+public:
+ trigger trig;
+ trigthread(diagthread* idiag)
+ : thread(false), diag(idiag), myname("trigthread"), trig(true, false) {}
+ virtual ~trigthread() { waitfor(); }
+};
+
+
+void trigthread::execute()
+{
+ diag->postdiag(myname, "waits on the trigger");
+ trig.wait();
+
+ psleep(2000);
+ diag->postdiag(myname, "waits on the trigger");
+ trig.wait();
+
+ diag->postdiag(myname, "terminates");
+}
+
+
+int trigger_test()
+{
+ pout.put("\n--- TRIGGER\n");
+
+ diagthread diag;
+ trigthread thr(&diag);
+
+ string myname = "main";
+
+ diag.start();
+ thr.start();
+
+ psleep(1000);
+ diag.postdiag(myname, "posts the trigger");
+ thr.trig.post();
+
+ psleep(1000);
+ diag.postdiag(myname, "posts the trigger again");
+ thr.trig.post();
+
+ thr.waitfor();
+ diag.postquit();
+ diag.waitfor();
+ return 0;
+}
+
+
+
+#endif // PTYPES_ST
+
+
+//
+// md5 test
+//
+
+static md5_digest digest;
+
+char* md5str(string data)
+{
+ outmd5 m;
+ m.open();
+ m.put(data);
+ memcpy(digest, m.get_bindigest(), sizeof(md5_digest));
+ return (char*)digest;
+}
+
+string cryptpw(string username, string password)
+{
+ outmd5 m;
+ m.open();
+ m.put(username);
+ m.put(password);
+ m.close();
+ return m.get_digest();
+}
+
+void md5_test()
+{
+ pout.put("\n--- MD5 OUTPUT STREAM\n");
+ // MD5 test suite from RFC1321
+ showhex("d41d8cd98f00b204e9800998ecf8427e", md5str(""), md5_digsize);
+ showhex("0cc175b9c0f1b6a831c399e269772661", md5str("a"), md5_digsize);
+ showhex("900150983cd24fb0d6963f7d28e17f72", md5str("abc"), md5_digsize);
+ showhex("f96b697d7cb7938d525a2f31aaf161d0", md5str("message digest"), md5_digsize);
+ showhex("c3fcd3d76192e4007dfb496cca67e13b", md5str("abcdefghijklmnopqrstuvwxyz"), md5_digsize);
+ showhex("d174ab98d277d9f5a5611c2c9f419d9f", md5str("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"), md5_digsize);
+ showhex("57edf4a22be3c955ac49da2e2107b67a", md5str("12345678901234567890123456789012345678901234567890123456789012345678901234567890"), md5_digsize);
+
+ showstr("t0htL.C9vunX8SPPsJjDmk", cryptpw("hovik", "myfavoritelonglonglongpassword"));
+}
+
+
+//
+// outstm::putf() test
+//
+
+void putf_test()
+{
+ pout.put("\n--- PUTF TEST\n");
+ outmemory m;
+ m.open();
+
+ m.putf("%s, %c, %d, %llx", "string", 'A', 1234, large(-1));
+ showstr("string, A, 1234, ffffffffffffffff", m.get_strdata());
+
+ m.open();
+
+ m.putf(" %%, %#o, %+010d", 0765, -3);
+ showstr(" %, 0765, -000000003", m.get_strdata());
+}
+
+
+//
+// pinet/socket tests
+//
+
+
+void inet_test1()
+{
+ try
+ {
+ pout.put("\n--- INET SOCKET & UTILITIES\n");
+
+ ipaddress ip(127, 0, 0, 1);
+
+ // as a test target host we use the one that would never be down! :)
+ string testname = "www.apache.org";
+
+ ip = phostbyname(testname);
+ string ips = iptostring(ip);
+ pout.putf("IP address of %s: %s\n", pconst(testname), (ip == ipnone) ? "failed" : pconst(ips));
+
+ if (ip != ipnone)
+ {
+ string hs = phostbyaddr(ip);
+ pout.putf("Name of %s: %s\n", pconst(ips), pconst(hs));
+ }
+
+ pout.putf("Canonical name of your local www: %s\n", pconst(phostcname("www")));
+
+ testname = "www.melikyan.com";
+ pout.putf("\nTrying %s:80...\n", pconst(testname));
+ ipstream s(testname, 80);
+
+ const char* request =
+ "GET /ptypes/test.txt HTTP/1.1\r\n"
+ "Accept: * /*\r\n"
+ "User-Agent: ptypes_test/2.1\r\n"
+ "Host: www.melikyan.com\r\n"
+ "Connection: close\r\n\r\n";
+
+ s.open();
+ s.put(request);
+ s.flush();
+
+ while (!s.get_eof())
+ {
+ char buf[16];
+ int r = s.read(buf, sizeof(buf));
+ pout.write(buf, r);
+ }
+ pout.put("\n");
+
+ s.close();
+ }
+ catch(estream* e)
+ {
+ perr.putf("Socket error: %s\n", pconst(e->get_message()));
+ delete e;
+ }
+}
+
+
+#ifndef PTYPES_ST
+
+
+const int testport = 8085;
+
+
+class svthread: public thread, protected ipstmserver
+{
+protected:
+ virtual void execute(); // override thread::execute()
+ virtual void cleanup(); // override thread::cleanup()
+public:
+ svthread(): thread(false) {}
+ virtual ~svthread();
+};
+
+
+svthread::~svthread()
+{
+ waitfor();
+}
+
+
+void svthread::execute()
+{
+ ipstream client; // a socket object to communicate with the client
+
+ try
+ {
+ bindall(testport); // listen to all local addresses on port 8081
+ serve(client); // wait infinitely for a connection request
+
+ if (client.get_active())
+ {
+ // read one line from the stream; note that theoretically the line can be long,
+ // so calling client.line(buf, sizeof(buf)) here is a much better idea
+ string req = lowercase(client.line());
+ if (req == "hello")
+ {
+ // try to reverse-lookup the client's IP
+ string host = phostbyaddr(client.get_ip());
+ if (isempty(host))
+ host = iptostring(client.get_ip());
+
+ // now send our greeting to the client
+ client.putf("Hello, %s (%a), nice to see you!\n",
+ pconst(host), long(client.get_ip()));
+ client.flush();
+ }
+
+ // close() should be called explicitly to shut down the socket
+ // *gracefully*; otherwise ipstream's destructor may close the
+ // socket but in a less polite manner
+ client.close();
+ }
+ }
+ catch(estream* e)
+ {
+ perr.putf("Server error: %s\n", pconst(e->get_message()));
+ delete e;
+ }
+
+ // a real server could enter an infinite loop serving requests
+ // and producing separate threads for each connection
+}
+
+
+void svthread::cleanup()
+{
+}
+
+
+void inet_test2()
+{
+ pout.put("\n--- INET CLIENT/SERVER\n");
+
+ // we run the server in a separate thread in order to be able
+ // to imitate a client connection from the main thread
+ svthread server;
+
+ pout.put("\nStarting the server thread...\n");
+ server.start(); // it's that easy! :)
+
+ // sleep some time to let the server start its job
+ psleep(1000);
+
+ try
+ {
+ // now create a client socket and send a greeting to our server
+ ipstream client(ipaddress(127, 0, 0, 1), testport);
+ client.open();
+
+ pout.put("Sending a request to the server...\n");
+ client.putline("Hello");
+ client.flush();
+ string rsp = client.line();
+ pout.putf("Received: %s\n", pconst(rsp));
+ pout.putf("My address and port: %s:%d\n",
+ pconst(iptostring(client.get_myip())), client.get_myport());
+
+ client.close();
+ }
+ catch(estream* e)
+ {
+ perr.putf("Error: %s\n", pconst(e->get_message()));
+ delete e;
+ }
+
+}
+
+
+//
+// UDP test
+//
+
+
+class msgsvthread: public thread
+{
+protected:
+ void execute();
+public:
+ msgsvthread(): thread(false) {}
+ virtual ~msgsvthread() { waitfor(); }
+};
+
+
+void msgsvthread::execute()
+{
+ ipmsgserver s;
+ s.bindall(testport);
+ try
+ {
+ string req = s.receive(1024);
+ pout.putf("Server received: %s\n", pconst(req));
+ string rsp = "gotcha";
+ s.send(rsp);
+ }
+ catch(estream* e)
+ {
+ perr.putf("Server error: %s\n", pconst(e->get_message()));
+ delete e;
+ }
+}
+
+
+void inet_test3()
+{
+ pout.put("\n--- INET MESSAGE CLIENT/SERVER\n");
+
+ msgsvthread sv;
+ sv.start();
+ psleep(1000);
+
+ ipmessage m(ipbcast /* ipaddress(127, 0, 0, 1) */, testport);
+ try
+ {
+ string msg = "hello";
+ m.send(msg);
+ string rsp = m.receive(1024);
+ pout.putf("Client received: %s\n", pconst(rsp));
+ }
+ catch(estream* e)
+ {
+ perr.putf("Client error: %s\n", pconst(e->get_message()));
+ delete e;
+ }
+}
+
+
+//
+// named pipes test
+//
+
+
+#define TEST_PIPE "ptypes.test"
+
+
+class npthread: public thread, protected npserver
+{
+protected:
+ virtual void execute();
+ virtual void cleanup();
+public:
+ npthread(): thread(false), npserver(TEST_PIPE) {}
+ virtual ~npthread();
+};
+
+
+npthread::~npthread()
+{
+ waitfor();
+}
+
+
+void npthread::execute()
+{
+ namedpipe client;
+
+ try
+ {
+ serve(client);
+
+ if (client.get_active())
+ {
+ string req = lowercase(client.line());
+ if (req == "hello")
+ {
+ client.putline("Hello, nice to see you!");
+ client.flush();
+ }
+
+ client.close();
+ }
+ }
+ catch(estream* e)
+ {
+ perr.putf("Pipe server error: %s\n", pconst(e->get_message()));
+ delete e;
+ }
+}
+
+
+void npthread::cleanup()
+{
+}
+
+
+void pipe_test()
+{
+ npthread server;
+
+ pout.put("\n--- NAMED PIPES\n");
+ pout.put("Starting the pipe server thread...\n");
+ server.start();
+
+ psleep(1000);
+
+ namedpipe client(TEST_PIPE);
+
+ try
+ {
+ client.open();
+
+ pout.put("Sending a request to the server...\n");
+ client.putline("Hello");
+ client.flush();
+ string rsp = client.line();
+ pout.putf("Received: %s\n", pconst(rsp));
+
+ client.close();
+ }
+ catch(estream* e)
+ {
+ perr.putf("Error: %s\n", pconst(e->get_message()));
+ delete e;
+ }
+}
+
+
+#endif // PTYPES_ST
+
+
+//
+// date/time/calendar
+//
+
+
+void time_test()
+{
+ pout.put("\n--- DATE/TIME/CALENDAR\n");
+
+ tzupdate();
+
+ int year, month, day;
+ datetime d = encodedate(9999, 12, 31);
+ decodedate(d, year, month, day);
+ d = encodedate(1970, 1, 1);
+ decodedate(d, year, month, day);
+
+ datetime dt;
+ dt = invdatetime;
+ int hour, min, sec, msec;
+ dt = encodetime(23, 59, 59, 998);
+ decodetime(dt, hour, min, sec, msec);
+
+ dt = encodedate(2001, 8, 27) + encodetime(14, 33, 10);
+ d = encodedate(2001, 8, 28) + encodetime(14, 33, 10, 500);
+ dayofweek(dt);
+
+ dt = now(false);
+ pout.putf("Local time: %s\n", pconst(dttostring(dt, "%x %X %Z")));
+ datetime utc = now();
+ pout.putf("UTC time: %t GMT\n", utc);
+
+ time_t ut;
+ time(&ut);
+ pout.putline(dttostring(utodatetime(ut), "%c"));
+
+ int t = tzoffset();
+ bool neg = t < 0;
+ if (neg)
+ t = -t;
+ pout.putf("Time zone offset: %c%02d%02d\n", neg ? '-' : '+', t / 60, t % 60);
+ {
+ // PTypes' birthday (birth moment, if you wish)
+ datetime d = encodedate(2000, 3, 30) + encodetime(13, 24, 58, 995);
+ pout.putf("PTypes' birth moment: %T GMT\n", d);
+
+ // now see how old is PTypes in days, hours, minutes, etc
+ datetime diff = now() - d;
+ int hours, mins, secs, msecs;
+ decodetime(diff, hours, mins, secs, msecs);
+ pout.putf("PTypes' life time: %d days %d hours %d minutes %d seconds and %d milliseconds\n",
+ days(diff), hours, mins, secs, msecs);
+
+#ifndef PTYPES_ST
+ // measure the difference in milliseconds between two calls to now()
+ datetime m = now();
+ psleep(17); // sleep for 17 milliseconds
+ pout.putf("A 17 millisecond dream lasted actually %d milliseconds\n", int(now() - m));
+ // this will show the actual precision of the clock on the given platform;
+ // Windows, f.ex., always shows the difference in 10 msec increments
+#endif
+ }
+}
+
+
+//
+// streams documentation example #2
+//
+
+
+const cset letters("_A-Za-z");
+const cset digits("0-9");
+const cset identchars = letters + digits;
+const cset otherchars = !letters;
+
+
+void doc_example()
+{
+ tstrlist<void*> dic(SL_SORTED);
+
+ infile f("../src/ptypes_test.cxx");
+
+ try
+ {
+ f.open();
+
+ while (!f.get_eof())
+ {
+ char c = f.preview();
+
+ // a C identifier starts with a letter
+ if (c & letters)
+ {
+ // ... and may contain letters and digits
+ string ident = f.token(identchars);
+ int i;
+ if (!dic.search(ident, i))
+ dic.add(ident, 0);
+ }
+
+ else
+ f.skiptoken(otherchars);
+ }
+
+ }
+
+ catch (estream* e)
+ {
+ pout.putf("Error: %s\n", pconst(e->get_message()));
+ delete e;
+ }
+
+ // now print the dictionary
+ for (int i = 0; i < dic.get_count(); i++)
+ pout.putline(dic.getkey(i));
+}
+
+
+//
+// variant
+//
+
+
+void variant_test()
+{
+ pout.put("\n--- VARIANT\n");
+
+ variant v0 = 'A';
+ variant v1 = short(33);
+ variant v2 = "456";
+ variant v3 = int(v1) + int(v2);
+ variant v4 = string(v1) + " cows";
+ string s = string(v4);
+// s = v4;
+ variant v5 = new component();
+ variant v6;
+
+ put(v6, 291, v1);
+ put(v6, "key", v2);
+ put(v6, "another-key", "another-value");
+ showstr("33 cows", string(v4));
+ showint(456, v6["key"]);
+ showstr("33", string(v1));
+ showint(1, bool(v6));
+ v2 = aclone(v6);
+ v5 = v6;
+
+ variant vi;
+ int i;
+ for (i = 0; anext(v6, i, vi);)
+ pout.putf("%d\n", int(vi));
+
+ variant v7;
+ aadd(v7, v1);
+ aadd(v7, v3);
+ aadd(v7, v4);
+ adel(v7, 2);
+ for (i = 0; i < alength(v7); i++)
+ pout.putf("%s\n", pconst(string(aget(v7, i))));
+}
+
+
+//
+// main
+//
+
+
+int main()
+{
+ try
+ {
+ string_test1();
+ string_test2();
+ string_benchmarks();
+ cset_test();
+
+ podlist_test();
+ ptrlist_test();
+ strlist_test();
+ textmap_test();
+
+ outfile_test();
+ infile_test();
+ mem_test();
+ md5_test();
+ putf_test();
+
+ time_test();
+ variant_test();
+
+ inet_test1();
+
+#ifndef PTYPES_ST
+ pipe_test();
+
+ jobqueue_test();
+ thread_test();
+ rwlock_test();
+ trigger_test();
+
+ inet_test2();
+ inet_test3();
+#endif
+
+ }
+
+ catch (estream* e)
+ {
+ perr.putf("\nError: %s\n", pconst(e->get_message()));
+ exit(1);
+ delete e;
+ }
+
+#ifdef DEBUG
+ if (stralloc != 0 || objalloc != 0)
+ {
+ perr.putf("DBG stralloc: %d, objalloc: %d\n", stralloc, objalloc);
+ fatal(255, "Allocation problems");
+ }
+#endif
+
+ return 0;
+}
+
diff --git a/source/ptypes/punit.cxx b/source/ptypes/punit.cxx
@@ -0,0 +1,176 @@
+/*
+ *
+ * C++ Portable Types Library (PTypes)
+ * Version 2.1.1 Released 27-Jun-2007
+ *
+ * Copyright (C) 2001-2007 Hovik Melikyan
+ *
+ * http://www.melikyan.com/ptypes/
+ *
+ */
+
+#include "ptypes.h"
+#include "pasync.h"
+#include "pstreams.h"
+
+#ifdef WIN32
+# include <windows.h>
+#else
+# include <unistd.h>
+#endif
+
+
+namespace ptypes {
+
+
+//
+// internal thread class for running units asynchronously
+//
+
+class unit_thread: public thread
+{
+protected:
+ unit* target;
+ virtual void execute();
+public:
+ unit_thread(unit* itarget);
+ virtual ~unit_thread();
+};
+
+
+unit_thread::unit_thread(unit* itarget)
+ : thread(false), target(itarget)
+{
+ start();
+}
+
+
+
+unit_thread::~unit_thread()
+{
+ waitfor();
+}
+
+
+void unit_thread::execute()
+{
+ target->do_main();
+}
+
+
+//
+// unit class
+//
+
+unit::unit()
+ : component(), pipe_next(nil), main_thread(nil),
+ running(0), uin(&pin), uout(&pout)
+{
+}
+
+
+unit::~unit()
+{
+ delete tpexchange<unit_thread>(&main_thread, nil);
+}
+
+
+int unit::classid()
+{
+ return CLASS_UNIT;
+}
+
+
+void unit::main()
+{
+}
+
+
+void unit::cleanup()
+{
+}
+
+
+void unit::do_main()
+{
+ try
+ {
+ if (!uout->get_active())
+ uout->open();
+ if (!uin->get_active())
+ uin->open();
+ main();
+ if (uout->get_active())
+ uout->flush();
+ }
+ catch(exception* e)
+ {
+ perr.putf("Error: %s\n", pconst(e->get_message()));
+ delete e;
+ }
+
+ try
+ {
+ cleanup();
+ }
+ catch(exception* e)
+ {
+ perr.putf("Error: %s\n", pconst(e->get_message()));
+ delete e;
+ }
+
+ if (pipe_next != nil)
+ uout->close();
+}
+
+
+void unit::connect(unit* next)
+{
+ waitfor();
+ pipe_next = next;
+ infile* in = new infile();
+ outfile* out = new outfile();
+ next->uin = in;
+ uout = out;
+ in->pipe(*out);
+}
+
+
+void unit::waitfor()
+{
+ if (running == 0)
+ return;
+ delete tpexchange<unit_thread>(&main_thread, nil);
+ unit* next = tpexchange<unit>(&pipe_next, nil);
+ if (next != nil)
+ {
+ next->waitfor();
+ next->uin = &pin;
+ }
+ uout = &pout;
+ running = 0;
+}
+
+
+void unit::run(bool async)
+{
+ if (pexchange(&running, 1) != 0)
+ return;
+
+ if (main_thread != nil)
+ fatal(CRIT_FIRST + 60, "Unit already running");
+
+ if (pipe_next != nil)
+ pipe_next->run(true);
+
+ if (async)
+ main_thread = new unit_thread(this);
+ else
+ {
+ do_main();
+ waitfor();
+ }
+}
+
+
+}
diff --git a/source/ptypes/punknown.cxx b/source/ptypes/punknown.cxx
@@ -0,0 +1,21 @@
+/*
+ *
+ * C++ Portable Types Library (PTypes)
+ * Version 2.1.1 Released 27-Jun-2007
+ *
+ * Copyright (C) 2001-2007 Hovik Melikyan
+ *
+ * http://www.melikyan.com/ptypes/
+ *
+ */
+
+#include "ptypes.h"
+
+
+namespace ptypes {
+
+
+int objalloc = 0;
+
+
+}
diff --git a/source/ptypes/pvariant.cxx b/source/ptypes/pvariant.cxx
@@ -0,0 +1,641 @@
+/*
+ *
+ * C++ Portable Types Library (PTypes)
+ * Version 2.1.1 Released 27-Jun-2007
+ *
+ * Copyright (C) 2001-2007 Hovik Melikyan
+ *
+ * http://www.melikyan.com/ptypes/
+ *
+ */
+
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <limits.h>
+
+#include "ptypes.h"
+
+
+namespace ptypes {
+
+
+const variant nullvar;
+
+
+struct _varitem
+{
+ string key;
+ variant var;
+
+ _varitem(const string& ikey, const variant& ivar): key(ikey), var(ivar) {}
+};
+typedef _varitem* pvaritem;
+
+
+class ptpublic _varray: protected tobjlist<_varitem>
+{
+protected:
+ int refcount;
+
+ virtual int compare(const void* key, const void* item) const;
+
+ friend class variant;
+
+public:
+ _varray();
+ _varray(const _varray& a);
+ virtual ~_varray();
+
+ int get_count() { return tobjlist<_varitem>::get_count(); }
+ void clear() { tobjlist<_varitem>::clear(); }
+ void pack() { tobjlist<_varitem>::pack(); }
+ _varitem* doget(int index) const { return tobjlist<_varitem>::doget(index); }
+ const variant& get(int index) const { if (unsigned(index) < unsigned(count)) return doget(index)->var; else return nullvar; }
+ const string& getkey(int index) const { if (unsigned(index) < unsigned(count)) return doget(index)->key; else return nullstring; }
+ const variant& get(const char* key) const;
+ int put(const string& key, const variant& var);
+ void put(int index, const variant& var) { if (unsigned(index) < unsigned(count)) doget(index)->var = var; }
+ void ins(int index, const variant& var) { if (unsigned(index) < unsigned(count)) doins(index, new _varitem(nullstring, var)); }
+ int addvar(const variant& var);
+ void del(int index) { if (unsigned(index) < unsigned(count)) dodel(index); }
+ void del(const string& key) { put(key, nullstring); }
+};
+typedef _varray* pvarray;
+
+
+_varray::_varray()
+ : tobjlist<_varitem>(true), refcount(0)
+{
+ config.sorted = true;
+ config.casesens = true;
+}
+
+
+_varray::_varray(const _varray& a)
+ : tobjlist<_varitem>(true), refcount(0)
+{
+ config.sorted = true;
+ config.casesens = true;
+ set_capacity(a.count);
+ for (int i = 0; i < a.count; i++)
+ {
+ _varitem* v = a.doget(i);
+ doins(i, new _varitem(v->key, v->var));
+ }
+}
+
+
+_varray::~_varray()
+{
+}
+
+
+int _varray::compare(const void* key, const void* item) const
+{
+ if (config.casesens)
+ return strcmp(pconst(key), pvaritem(item)->key);
+ else
+ return strcasecmp(pconst(key), pvaritem(item)->key);
+}
+
+
+const variant& _varray::get(const char* key) const
+{
+ int index;
+ if (search(key, index))
+ return doget(index)->var;
+ else
+ return nullvar;
+}
+
+
+int _varray::put(const string& key, const variant& var)
+{
+ int index;
+ if (search(pconst(key), index))
+ {
+ if (isnull(var))
+ dodel(index);
+ else
+ doget(index)->var = var;
+ }
+ else if (!isnull(var))
+ doins(index, new _varitem(key, var));
+ return index;
+}
+
+
+int _varray::addvar(const variant& v)
+{
+ int i;
+ if (count > 0 && isempty(doget(count - 1)->key))
+ i = count;
+ else
+ i = 0;
+ doins(i, new _varitem(nullstring, v));
+ return i;
+}
+
+
+static void vconverr(large v);
+
+
+static void vfatal()
+{
+ fatal(CRIT_FIRST + 60, "Variant data corrupt");
+}
+
+
+evariant::~evariant()
+{
+}
+
+
+void variant::initialize(_varray* a)
+{
+ tag = VAR_ARRAY;
+#ifdef PTYPES_ST
+ a->refcount++;
+#else
+ pincrement(&a->refcount);
+#endif
+ value.a = a;
+}
+
+
+void variant::initialize(component* o)
+{
+ tag = VAR_OBJECT;
+ value.o = addref(o);
+}
+
+
+void variant::initialize(const variant& v)
+{
+ switch (v.tag)
+ {
+ case VAR_NULL:
+ tag = VAR_NULL;
+ break;
+ case VAR_INT:
+ case VAR_BOOL:
+ case VAR_FLOAT:
+ tag = v.tag;
+ value = v.value;
+ break;
+ case VAR_STRING:
+ initialize(PTR_TO_STRING(v.value.s));
+ break;
+ case VAR_ARRAY:
+ initialize(v.value.a);
+ break;
+ case VAR_OBJECT:
+ initialize(v.value.o);
+ break;
+ default:
+ vfatal();
+ }
+}
+
+
+void variant::finalize()
+{
+ if (tag >= VAR_COMPOUND)
+ {
+ switch (tag)
+ {
+ case VAR_STRING:
+ ptypes::finalize(PTR_TO_STRING(value.s));
+ break;
+ case VAR_ARRAY:
+#ifdef PTYPES_ST
+ if (--value.a->refcount == 0)
+#else
+ if (pdecrement(&value.a->refcount) == 0)
+#endif
+ delete value.a;
+ break;
+ case VAR_OBJECT:
+ release(value.o);
+ break;
+ default:
+ vfatal();
+ }
+ }
+ tag = VAR_NULL;
+}
+
+
+void variant::assign(large v) { finalize(); initialize(v); }
+void variant::assign(bool v) { finalize(); initialize(v); }
+void variant::assign(double v) { finalize(); initialize(v); }
+void variant::assign(const char* v) { finalize(); initialize(v); }
+
+
+void variant::assign(const string& v)
+{
+ if (tag == VAR_STRING)
+ PTR_TO_STRING(value.s) = v;
+ else
+ {
+ finalize();
+ initialize(v);
+ }
+}
+
+
+void variant::assign(_varray* a)
+{
+ if (tag == VAR_ARRAY && value.a == a)
+ return;
+ finalize();
+ initialize(a);
+}
+
+
+void variant::assign(component* o)
+{
+ if (tag == VAR_OBJECT)
+ {
+ if (value.o == o)
+ return;
+ else
+ release(value.o);
+ }
+ else
+ finalize();
+ initialize(o);
+}
+
+
+void variant::assign(const variant& v)
+{
+ switch (v.tag)
+ {
+ case VAR_NULL:
+ finalize();
+ tag = VAR_NULL;
+ break;
+ case VAR_INT:
+ case VAR_BOOL:
+ case VAR_FLOAT:
+ finalize();
+ tag = v.tag;
+ value = v.value;
+ break;
+ case VAR_STRING:
+ assign(PTR_TO_STRING(v.value.s));
+ break;
+ case VAR_ARRAY:
+ assign(v.value.a);
+ break;
+ case VAR_OBJECT:
+ assign(v.value.o);
+ break;
+ default:
+ vfatal();
+ }
+}
+
+
+void ptdecl clear(variant& v)
+{
+ v.finalize();
+ v.initialize();
+}
+
+
+variant::operator int() const
+{
+ large t = operator large();
+ if (t < INT_MIN || t > INT_MAX)
+ vconverr(t);
+ return int(t);
+}
+
+
+variant::operator unsigned int() const
+{
+ large t = operator large();
+ if (t < 0 || t > UINT_MAX)
+ vconverr(t);
+ return uint(t);
+}
+
+
+variant::operator long() const
+{
+ large t = operator large();
+ if (t < LONG_MIN || t > LONG_MAX)
+ vconverr(t);
+ return int(t);
+}
+
+
+variant::operator unsigned long() const
+{
+ large t = operator large();
+ if (t < 0 || t > large(ULONG_MAX))
+ vconverr(t);
+ return uint(t);
+}
+
+
+variant::operator large() const
+{
+ switch(tag)
+ {
+ case VAR_NULL: return 0;
+ case VAR_INT: return value.i;
+ case VAR_BOOL: return int(value.b);
+ case VAR_FLOAT: return int(value.f);
+ case VAR_STRING:
+ {
+ const char* p = PTR_TO_STRING(value.s);
+ bool neg = *p == '-';
+ if (neg)
+ p++;
+ large t = stringtoi(p);
+ if (t < 0)
+ return 0;
+ else
+ return neg ? -t : t;
+ }
+ case VAR_ARRAY: return value.a->count != 0;
+ case VAR_OBJECT: return 0;
+ default: vfatal();
+ }
+ return 0;
+}
+
+
+variant::operator bool() const
+{
+ switch(tag)
+ {
+ case VAR_NULL: return false;
+ case VAR_INT: return value.i != 0;
+ case VAR_BOOL: return value.b;
+ case VAR_FLOAT: return value.f != 0;
+ case VAR_STRING: return !isempty((PTR_TO_STRING(value.s)));
+ case VAR_ARRAY: return value.a->count != 0;
+ case VAR_OBJECT: return value.o != nil;
+ default: vfatal();
+ }
+ return false;
+}
+
+
+variant::operator double() const
+{
+ switch(tag)
+ {
+ case VAR_NULL: return 0;
+ case VAR_INT: return double(value.i);
+ case VAR_BOOL: return int(value.b);
+ case VAR_FLOAT: return value.f;
+ case VAR_STRING:
+ {
+ char* e;
+ double t = strtod(PTR_TO_STRING(value.s), &e);
+ if (*e != 0)
+ return 0;
+ else
+ return t;
+ }
+ case VAR_ARRAY: return int(value.a->count != 0);
+ case VAR_OBJECT: return 0;
+ default: vfatal();
+ }
+ return 0;
+}
+
+
+void string::initialize(const variant& v)
+{
+ switch(v.tag)
+ {
+ case VAR_NULL: initialize(); break;
+ case VAR_INT: initialize(itostring(v.value.i)); break;
+ case VAR_BOOL: if (v.value.b) initialize('1'); else initialize('0'); break;
+ case VAR_FLOAT:
+ {
+ char buf[256];
+ sprintf(buf, "%g", v.value.f);
+ initialize(buf);
+ }
+ break;
+ case VAR_STRING: initialize(PTR_TO_STRING(v.value.s)); break;
+ case VAR_ARRAY: initialize(); break;
+ case VAR_OBJECT: initialize(); break;
+ default: vfatal();
+ }
+}
+
+
+variant::operator string() const
+{
+ // this is a 'dirty' solution to gcc 3.3 typecast problem. most compilers
+ // handle variant::operator string() pretty well, while gcc 3.3 requires
+ // to explicitly declare a constructor string::string(const variant&).
+ // ironically, the presence of both the typecast operator and the constructor
+ // confuses the MSVC compiler. so the only thing we can do to please all
+ // those compilers [that "move towards the c++ standard"] is to conditionally
+ // exclude the constructor string(const variant&). and this is not the whole
+ // story. i see you are bored with it and i let you go. nobody would ever care
+ // about this. it just works, though i'm not happy with what i wrote here:
+ string t;
+ t.initialize(*this);
+ return t;
+}
+
+
+variant::operator component*() const
+{
+ if (tag == VAR_OBJECT)
+ return value.o;
+ else
+ return nil;
+}
+
+
+bool variant::equal(const variant& v) const
+{
+ if (tag != v.tag)
+ return false;
+ switch (tag)
+ {
+ case VAR_NULL: return true;
+ case VAR_INT: return value.i == v.value.i;
+ case VAR_BOOL: return value.b == v.value.b;
+ case VAR_FLOAT: return value.f == v.value.f;
+ case VAR_STRING: return strcmp(value.s, v.value.s) == 0;
+ case VAR_ARRAY: return value.a == v.value.a;
+ case VAR_OBJECT: return value.o == v.value.o;
+ default: vfatal(); return false;
+ }
+}
+
+
+static string numkey(large key)
+{
+ return itostring(key, 16, 16, '0');
+}
+
+
+void ptdecl aclear(variant& v)
+{
+ if (v.tag == VAR_ARRAY)
+ v.value.a->clear();
+ else
+ {
+ v.finalize();
+ v.initialize(new _varray());
+ }
+}
+
+
+void ptdecl apack(variant& v)
+{
+ if (v.tag == VAR_ARRAY)
+ v.value.a->pack();
+}
+
+
+variant ptdecl aclone(const variant& v)
+{
+ if (v.tag == VAR_ARRAY)
+ return variant(new _varray(*(v.value.a)));
+ else
+ return variant(new _varray());
+}
+
+
+int ptdecl alength(const variant& v)
+{
+ if (v.tag == VAR_ARRAY)
+ return v.value.a->get_count();
+ else
+ return 0;
+}
+
+
+const variant& ptdecl get(const variant& v, const string& key)
+{
+ if (v.tag == VAR_ARRAY)
+ return v.value.a->get(key);
+ else
+ return nullvar;
+}
+
+
+const variant& ptdecl get(const variant& v, large key)
+{
+ return get(v, numkey(key));
+}
+
+
+void ptdecl put(variant& v, const string& key, const variant& item)
+{
+ if (v.tag != VAR_ARRAY)
+ aclear(v);
+ v.value.a->put(key, item);
+}
+
+
+void ptdecl put(variant& v, large key, const variant& item)
+{
+ put(v, numkey(key), item);
+}
+
+
+void ptdecl del(variant& v, const string& key)
+{
+ if (v.tag == VAR_ARRAY)
+ v.value.a->del(key);
+}
+
+
+void ptdecl del(variant& v, large key)
+{
+ del(v, numkey(key));
+}
+
+
+bool ptdecl anext(const variant& array, int& index, variant& item)
+{
+ string key;
+ return anext(array, index, item, key);
+}
+
+
+bool ptdecl anext(const variant& array, int& index, variant& item, string& key)
+{
+ if (array.tag != VAR_ARRAY)
+ {
+ clear(item);
+ return false;
+ }
+ if (index < 0 || index >= array.value.a->get_count())
+ {
+ clear(item);
+ return false;
+ }
+ item = array.value.a->doget(index)->var;
+ key = array.value.a->doget(index)->key;
+ index++;
+ return true;
+}
+
+
+int ptdecl aadd(variant& array, const variant& item)
+{
+ if (array.tag != VAR_ARRAY)
+ aclear(array);
+ return array.value.a->addvar(item);
+}
+
+
+const variant& ptdecl aget(const variant& array, int index)
+{
+ if (array.tag == VAR_ARRAY)
+ return array.value.a->get(index);
+ else
+ return nullvar;
+}
+
+
+void ptdecl adel(variant& array, int index)
+{
+ if (array.tag == VAR_ARRAY)
+ array.value.a->del(index);
+}
+
+
+void ptdecl aput(variant& array, int index, const variant& item)
+{
+ if (array.tag == VAR_ARRAY)
+ array.value.a->put(index, item);
+}
+
+
+void ptdecl ains(variant& array, int index, const variant& item)
+{
+ if (array.tag == VAR_ARRAY)
+ array.value.a->ins(index, item);
+}
+
+
+#ifdef _MSC_VER
+// disable "unreachable code" warning for throw (known compiler bug)
+# pragma warning (disable: 4702)
+#endif
+
+static void vconverr(large v)
+{
+ throw new evariant("Value out of range: " + itostring(v));
+}
+
+
+}
diff --git a/source/ptypes/pversion.cxx b/source/ptypes/pversion.cxx
@@ -0,0 +1,15 @@
+/*
+ *
+ * C++ Portable Types Library (PTypes)
+ * Version 2.1.1 Released 27-Jun-2007
+ *
+ * Copyright (C) 2001-2007 Hovik Melikyan
+ *
+ * http://www.melikyan.com/ptypes/
+ *
+ */
+
+#include "pport.h"
+
+unsigned long __ptypes_version = 0x00020101;
+
diff --git a/source/synthLib/device.cpp b/source/synthLib/device.cpp
@@ -10,9 +10,16 @@ using namespace dsp56k;
namespace synthLib
{
- Device::Device() = default;
+ Device::Device(const DeviceCreateParams& _params) : m_createParams(_params) // NOLINT(modernize-pass-by-value) dll transition, do not mess with the input data
+ {
+ }
Device::~Device() = default;
+ ASMJIT_NOINLINE void Device::release(std::vector<SMidiEvent>& _events)
+ {
+ _events.clear();
+ }
+
void Device::dummyProcess(const uint32_t _numSamples)
{
std::vector<float> buf;
@@ -29,6 +36,8 @@ namespace synthLib
void Device::process(const TAudioInputs& _inputs, const TAudioOutputs& _outputs, const size_t _size, const std::vector<SMidiEvent>& _midiIn, std::vector<SMidiEvent>& _midiOut)
{
+ _midiOut.clear();
+
for (const auto& ev : _midiIn)
{
m_translatorOut.clear();
@@ -60,7 +69,9 @@ namespace synthLib
bool Device::isSamplerateSupported(const float& _samplerate) const
{
- for (const auto& sr : getSupportedSamplerates())
+ std::vector<float> srs;
+ getSupportedSamplerates(srs);
+ for (const auto& sr : srs)
{
if(std::fabs(sr - _samplerate) < 1.0f)
return true;
@@ -82,7 +93,8 @@ namespace synthLib
float Device::getDeviceSamplerateForHostSamplerate(const float _hostSamplerate) const
{
- const auto preferred = getPreferredSamplerates();
+ std::vector<float> preferred;
+ getPreferredSamplerates(preferred);
// if there is no choice we need to use the only one that is supported
if(preferred.size() == 1)
diff --git a/source/synthLib/device.h b/source/synthLib/device.h
@@ -2,6 +2,7 @@
#include <cstdint>
#include <cstddef>
+#include <string>
#include "audioTypes.h"
#include "deviceTypes.h"
@@ -10,12 +11,26 @@
#include "buildconfig.h"
#include "midiTranslator.h"
+#include "asmjit/core/api-config.h"
+
+#include "baseLib/md5.h"
+
namespace synthLib
{
+ struct DeviceCreateParams
+ {
+ float preferredSamplerate = 0.0f;
+ float hostSamplerate = 0.0f;
+ std::string romName;
+ std::vector<uint8_t> romData;
+ baseLib::MD5 romHash;
+ uint32_t customData = 0;
+ };
+
class Device
{
public:
- Device();
+ Device(const DeviceCreateParams& _params);
Device(const Device&) = delete;
Device(Device&&) = delete;
@@ -32,14 +47,14 @@ namespace synthLib
virtual uint32_t getInternalLatencyMidiToOutput() const { return 0; }
virtual uint32_t getInternalLatencyInputToOutput() const { return 0; }
- virtual std::vector<float> getSupportedSamplerates() const
+ virtual void getSupportedSamplerates(std::vector<float>& _dst) const
{
- return {getSamplerate()};
+ _dst.push_back(getSamplerate());
}
virtual float getSamplerate() const = 0;
- virtual std::vector<float> getPreferredSamplerates() const
+ virtual void getPreferredSamplerates(std::vector<float>& _dst) const
{
- return getSupportedSamplerates();
+ return getSupportedSamplerates(_dst);
}
bool isSamplerateSupported(const float& _samplerate) const;
@@ -49,6 +64,9 @@ namespace synthLib
float getDeviceSamplerate(float _preferredDeviceSamplerate, float _hostSamplerate) const;
float getDeviceSamplerateForHostSamplerate(float _hostSamplerate) const;
+ auto& getDeviceCreateParams() { return m_createParams; }
+ const auto& getDeviceCreateParams() const { return m_createParams; }
+
virtual bool isValid() const = 0;
#if SYNTHLIB_DEMO_MODE == 0
@@ -64,6 +82,9 @@ namespace synthLib
virtual uint32_t getDspClockPercent() const = 0;
virtual uint64_t getDspClockHz() const = 0;
+
+ ASMJIT_NOINLINE virtual void release(std::vector<SMidiEvent>& _events);
+
auto& getMidiTranslator() { return m_midiTranslator; }
protected:
@@ -72,8 +93,9 @@ namespace synthLib
virtual bool sendMidi(const SMidiEvent& _ev, std::vector<SMidiEvent>& _response) = 0;
void dummyProcess(uint32_t _numSamples);
-
+
private:
+ DeviceCreateParams m_createParams;
std::vector<SMidiEvent> m_midiIn;
uint32_t m_extraLatency = 0;
diff --git a/source/synthLib/deviceTypes.h b/source/synthLib/deviceTypes.h
@@ -12,7 +12,9 @@ namespace synthLib
{
Invalid = -1,
None = 0,
- Unknown = 1,
- FirmwareMissing = 2,
+ Unknown,
+ FirmwareMissing,
+ RemoteUdpConnectFailed,
+ RemoteTcpConnectFailed,
};
}
diff --git a/source/synthLib/plugin.cpp b/source/synthLib/plugin.cpp
@@ -11,11 +11,12 @@ namespace synthLib
{
constexpr uint8_t g_stateVersion = 1;
- Plugin::Plugin(Device* _device)
+ Plugin::Plugin(Device* _device, CallbackDeviceInvalid _callbackDeviceInvalid)
: m_resampler(_device->getChannelCountIn(), _device->getChannelCountOut())
, m_device(_device)
- , m_deviceSamplerate(_device->getSamplerate())
, m_midiClock(*this)
+ , m_deviceSamplerate(_device->getSamplerate())
+ , m_callbackDeviceInvalid(std::move(_callbackDeviceInvalid))
{
}
@@ -37,7 +38,7 @@ namespace synthLib
const auto sr = m_device->getDeviceSamplerate(_samplerate, m_hostSamplerate);
- if(sr == m_deviceSamplerate)
+ if(sr == m_deviceSamplerate) // NOLINT(clang-diagnostic-float-equal)
return true;
if(!m_device->setSamplerate(sr))
@@ -50,16 +51,16 @@ namespace synthLib
return true;
}
- void Plugin::setHostSamplerate(const float _samplerate, float _preferredDeviceSamplerate)
+ void Plugin::setHostSamplerate(const float _hostSamplerate, const float _preferredDeviceSamplerate)
{
std::lock_guard lock(m_lock);
- m_deviceSamplerate = m_device->getDeviceSamplerate(_preferredDeviceSamplerate, _samplerate);
+ m_deviceSamplerate = m_device->getDeviceSamplerate(_preferredDeviceSamplerate, _hostSamplerate);
m_device->setSamplerate(m_deviceSamplerate);
- m_resampler.setSamplerates(_samplerate, m_deviceSamplerate);
+ m_resampler.setSamplerates(_hostSamplerate, m_deviceSamplerate);
- m_hostSamplerate = _samplerate;
- m_hostSamplerateInv = _samplerate > 0 ? 1.0f / _samplerate : 0.0f;
+ m_hostSamplerate = _hostSamplerate;
+ m_hostSamplerateInv = _hostSamplerate > 0 ? 1.0f / _hostSamplerate : 0.0f;
updateDeviceLatency();
}
@@ -80,7 +81,12 @@ namespace synthLib
std::lock_guard lock(m_lock);
if(!m_device->isValid())
- return;
+ {
+ m_device = m_callbackDeviceInvalid(m_device);
+
+ if(!m_device || !m_device->isValid())
+ return;
+ }
processMidiInEvents();
processMidiClock(_bpm, _ppqPos, _isPlaying, _count);
@@ -120,7 +126,8 @@ namespace synthLib
m_device = _device;
m_device->setSamplerate(m_deviceSamplerate);
- setState(deviceState);
+ if(!deviceState.empty())
+ setState(deviceState);
// MIDI clock has to send the start event again, some device find it confusing and do strange things if there isn't any
m_midiClock.restart();
@@ -140,7 +147,7 @@ namespace synthLib
return m_device->getState(_state, _type);
}
- bool Plugin::setState(const std::vector<uint8_t>& _state)
+ bool Plugin::setState(const std::vector<uint8_t>& _state) const
{
if(!m_device)
return false;
diff --git a/source/synthLib/plugin.h b/source/synthLib/plugin.h
@@ -1,6 +1,7 @@
#pragma once
#include <mutex>
+#include <functional>
#include "midiTypes.h"
#include "resamplerInOut.h"
@@ -18,7 +19,9 @@ namespace synthLib
class Plugin
{
public:
- Plugin(Device* _device);
+ using CallbackDeviceInvalid = std::function<Device*(Device*)>;
+
+ Plugin(Device* _device, CallbackDeviceInvalid _callbackDeviceInvalid);
void addMidiEvent(const SMidiEvent& _ev);
@@ -42,7 +45,7 @@ namespace synthLib
#if !SYNTHLIB_DEMO_MODE
bool getState(std::vector<uint8_t>& _state, StateType _type) const;
- bool setState(const std::vector<uint8_t>& _state);
+ bool setState(const std::vector<uint8_t>& _state) const;
#endif
void insertMidiEvent(const SMidiEvent& _ev);
@@ -63,7 +66,7 @@ namespace synthLib
SMidiEvent m_pendingSysexInput;
ResamplerInOut m_resampler;
- mutable std::mutex m_lock;
+ mutable std::recursive_mutex m_lock;
mutable std::mutex m_lockAddMidiEvent;
Device* m_device;
@@ -83,5 +86,6 @@ namespace synthLib
uint32_t m_extraLatencyBlocks = 1;
float m_deviceSamplerate = 0.0f;
+ CallbackDeviceInvalid m_callbackDeviceInvalid;
};
}
diff --git a/source/synthLib/resamplerInOut.cpp b/source/synthLib/resamplerInOut.cpp
@@ -192,6 +192,10 @@ namespace synthLib
}
m_scaledInput.fillPointers(inputs);
}
+ else
+ {
+ inputs.fill(nullptr);
+ }
_processFunc(inputs, _outs, _numProcessedSamples, m_processedMidiIn, m_midiOut);
diff --git a/source/virusJucePlugin/Leds.cpp b/source/virusJucePlugin/Leds.cpp
@@ -1,5 +1,6 @@
#include "Leds.h"
+#include "VirusController.h"
#include "VirusEditor.h"
#include "VirusProcessor.h"
diff --git a/source/virusJucePlugin/VirusProcessor.cpp b/source/virusJucePlugin/VirusProcessor.cpp
@@ -1,8 +1,7 @@
#include "VirusProcessor.h"
#include "VirusEditorState.h"
#include "ParameterNames.h"
-
-#include "virusLib/romloader.h"
+#include "VirusController.h"
#include "baseLib/binarystream.h"
@@ -48,7 +47,7 @@ namespace virus
try
{
- synthLib::Device* device = createDevice();
+ synthLib::Device* device = pluginLib::Processor::createDevice(getDeviceType());
getPlugin().setDevice(device);
(void)m_device.release();
m_device.reset(device);
@@ -86,6 +85,15 @@ namespace virus
synthLib::Device* VirusProcessor::createDevice()
{
+ synthLib::DeviceCreateParams p;
+ getRemoteDeviceParams(p);
+ return new virusLib::Device(p, true);
+ }
+
+ void VirusProcessor::getRemoteDeviceParams(synthLib::DeviceCreateParams& _params) const
+ {
+ pluginLib::Processor::getRemoteDeviceParams(_params);
+
const auto* rom = getSelectedRom();
if(!rom || !rom->isValid())
@@ -94,7 +102,10 @@ namespace virus
throw synthLib::DeviceException(synthLib::DeviceError::FirmwareMissing, "A Virus TI firmware (.bin) is required, but was not found.");
throw synthLib::DeviceException(synthLib::DeviceError::FirmwareMissing, "A Virus A/B/C operating system (.bin or .mid) is required, but was not found.");
}
- return new virusLib::Device(*rom, getPreferredDeviceSamplerate(), getHostSamplerate(), true);
+
+ _params.romName = rom->getFilename();
+ _params.romData = rom->getRomFileData();
+ _params.customData = static_cast<uint32_t>(rom->getModel());
}
pluginLib::Controller* VirusProcessor::createController()
diff --git a/source/virusJucePlugin/VirusProcessor.h b/source/virusJucePlugin/VirusProcessor.h
@@ -5,8 +5,6 @@
#include "jucePluginLib/event.h"
-#include "VirusController.h"
-
#include "jucePluginEditorLib/pluginProcessor.h"
namespace virus
@@ -60,6 +58,7 @@ namespace virus
//
private:
synthLib::Device* createDevice() override;
+ void getRemoteDeviceParams(synthLib::DeviceCreateParams& _params) const override;
pluginLib::Controller* createController() override;
diff --git a/source/virusLib/device.cpp b/source/virusLib/device.cpp
@@ -15,9 +15,10 @@
namespace virusLib
{
- Device::Device(ROMFile _rom, const float _preferredDeviceSamplerate, const float _hostSamplerate, const bool _createDebugger/* = false*/)
- : m_rom(std::move(_rom))
- , m_samplerate(getDeviceSamplerate(_preferredDeviceSamplerate, _hostSamplerate))
+ Device::Device(const synthLib::DeviceCreateParams& _params, const bool _createDebugger/* = false*/)
+ : synthLib::Device(_params)
+ , m_rom(_params.romData, _params.romName, static_cast<DeviceModel>(_params.customData))
+ , m_samplerate(getDeviceSamplerate(_params.preferredSamplerate, _params.hostSamplerate))
{
m_frontpanelStateMidiEvent.source = synthLib::MidiEventSource::Internal;
@@ -76,7 +77,7 @@ namespace virusLib
m_dsp.reset();
}
- std::vector<float> Device::getSupportedSamplerates() const
+ void Device::getSupportedSamplerates(std::vector<float>& _dst) const
{
switch (m_rom.getModel())
{
@@ -84,25 +85,35 @@ namespace virusLib
case DeviceModel::A:
case DeviceModel::B:
case DeviceModel::C:
- return {12000000.0f / 256.0f};
+ _dst.push_back(12000000.0f / 256.0f);
+ break;
case DeviceModel::Snow:
case DeviceModel::TI:
case DeviceModel::TI2:
- return {32000.0f, 44100.0f, 48000.0f, 64000.0f, 88200.0f, 96000.0f};
+ _dst.push_back(32000.0f);
+ _dst.push_back(44100.0f);
+ _dst.push_back(48000.0f);
+ _dst.push_back(64000.0f);
+ _dst.push_back(88200.0f);
+ _dst.push_back(96000.0f);
+ break;
}
}
- std::vector<float> Device::getPreferredSamplerates() const
+ void Device::getPreferredSamplerates(std::vector<float>& _dst) const
{
switch (m_rom.getModel())
{
default:
case DeviceModel::ABC:
- return getSupportedSamplerates();
+ getSupportedSamplerates(_dst);
+ break;
case DeviceModel::Snow:
case DeviceModel::TI:
case DeviceModel::TI2:
- return {44100.0f, 48000.0f};
+ _dst.push_back(44100.0f);
+ _dst.push_back(48000.0f);
+ break;
}
}
diff --git a/source/virusLib/device.h b/source/virusLib/device.h
@@ -19,11 +19,11 @@ namespace virusLib
class Device final : public synthLib::Device
{
public:
- Device(ROMFile _rom, float _preferredDeviceSamplerate, float _hostSamplerate, bool _createDebugger = false);
+ Device(const synthLib::DeviceCreateParams& _params, bool _createDebugger = false);
~Device() override;
- std::vector<float> getSupportedSamplerates() const override;
- std::vector<float> getPreferredSamplerates() const override;
+ void getSupportedSamplerates(std::vector<float>& _dst) const override;
+ void getPreferredSamplerates(std::vector<float>& _dst) const override;
float getSamplerate() const override;
bool setSamplerate(float _samplerate) override;
diff --git a/source/virusLib/romfile.h b/source/virusLib/romfile.h
@@ -119,6 +119,8 @@ public:
const auto& getHash() const { return m_romDataHash; }
+ const auto& getRomFileData() const { return m_romFileData; }
+
private:
std::vector<Chunk> readChunks(std::istream& _file) const;
bool loadPresetFiles();
diff --git a/source/wLib/wDevice.cpp b/source/wLib/wDevice.cpp
@@ -4,7 +4,11 @@
namespace wLib
{
- bool Device::setDspClockPercent(uint32_t _percent)
+ Device::Device(const synthLib::DeviceCreateParams& _params): synthLib::Device(_params)
+ {
+ }
+
+ bool Device::setDspClockPercent(const uint32_t _percent)
{
auto* c = getDspEsxiClock();
if(!c)
diff --git a/source/wLib/wDevice.h b/source/wLib/wDevice.h
@@ -12,6 +12,8 @@ namespace wLib
{
class Device : public synthLib::Device
{
+ public:
+ explicit Device(const synthLib::DeviceCreateParams& _params);
bool setDspClockPercent(uint32_t _percent) override;
uint32_t getDspClockPercent() const override;
uint64_t getDspClockHz() const override;
diff --git a/source/wLib/wRom.h b/source/wLib/wRom.h
@@ -10,14 +10,14 @@ namespace wLib
{
public:
ROM() = default;
- explicit ROM(const std::string& _filename, const uint32_t _expectedSize, std::vector<uint8_t> _data = {}) : m_buffer(std::move(_data))
+ explicit ROM(const std::string& _filename, const uint32_t _expectedSize, std::vector<uint8_t> _data = {}) : m_buffer(std::move(_data)), m_filename(_filename)
{
if (m_buffer.size() != _expectedSize)
loadFromFile(_filename, _expectedSize);
}
virtual ~ROM() = default;
- const uint8_t* getData() const { return m_buffer.data(); }
+ const auto& getData() const { return m_buffer; }
bool isValid() const { return !m_buffer.empty(); }
virtual uint32_t getSize() const = 0;
diff --git a/source/xtJucePlugin/PluginProcessor.cpp b/source/xtJucePlugin/PluginProcessor.cpp
@@ -9,6 +9,8 @@
#include "xtLib/xtDevice.h"
+#include "xtLib/xtRomLoader.h"
+
class Controller;
namespace
@@ -52,7 +54,22 @@ namespace xtJucePlugin
synthLib::Device* AudioPluginAudioProcessor::createDevice()
{
- return new xt::Device();
+ synthLib::DeviceCreateParams p;
+ getRemoteDeviceParams(p);
+ return new xt::Device(p);
+ }
+
+ void AudioPluginAudioProcessor::getRemoteDeviceParams(synthLib::DeviceCreateParams& _params) const
+ {
+ Processor::getRemoteDeviceParams(_params);
+
+ const auto rom = xt::RomLoader::findROM();
+
+ if(rom.isValid())
+ {
+ _params.romData = rom.getData();
+ _params.romName = rom.getFilename();
+ }
}
pluginLib::Controller* AudioPluginAudioProcessor::createController()
diff --git a/source/xtJucePlugin/PluginProcessor.h b/source/xtJucePlugin/PluginProcessor.h
@@ -13,6 +13,7 @@ namespace xtJucePlugin
jucePluginEditorLib::PluginEditorState* createEditorState() override;
synthLib::Device* createDevice() override;
+ void getRemoteDeviceParams(synthLib::DeviceCreateParams& _params) const override;
pluginLib::Controller* createController() override;
diff --git a/source/xtJucePlugin/serverPlugin.cpp b/source/xtJucePlugin/serverPlugin.cpp
@@ -0,0 +1,9 @@
+// ReSharper disable once CppUnusedIncludeDirective
+#include "client/plugin.h"
+
+#include "xtLib/xtDevice.h"
+
+synthLib::Device* createBridgeDevice(const synthLib::DeviceCreateParams& _params)
+{
+ return new xt::Device(_params);
+}
diff --git a/source/xtLib/xt.cpp b/source/xtLib/xt.cpp
@@ -11,9 +11,9 @@
namespace xt
{
- Xt::Xt()
+ Xt::Xt(const std::vector<uint8_t>& _romData, const std::string& _romName)
{
- m_hw.reset(new Hardware());
+ m_hw.reset(new Hardware(_romData, _romName));
if(!isValid())
return;
diff --git a/source/xtLib/xt.h b/source/xtLib/xt.h
@@ -30,7 +30,7 @@ namespace xt
Lcd = 0x02,
};
- Xt();
+ Xt(const std::vector<uint8_t>& _romData, const std::string& _romName);
~Xt();
bool isValid() const;
diff --git a/source/xtLib/xtDevice.cpp b/source/xtLib/xtDevice.cpp
@@ -9,7 +9,7 @@ namespace mqLib
namespace xt
{
- Device::Device() : m_wavePreview(m_xt), m_state(m_xt, m_wavePreview), m_sysexRemote(m_xt)
+ Device::Device(const synthLib::DeviceCreateParams& _params) : wLib::Device(_params), m_xt(_params.romData, _params.romName), m_wavePreview(m_xt), m_state(m_xt, m_wavePreview), m_sysexRemote(m_xt)
{
while(!m_xt.isBootCompleted())
m_xt.process(8);
diff --git a/source/xtLib/xtDevice.h b/source/xtLib/xtDevice.h
@@ -16,7 +16,7 @@ namespace xt
class Device : public wLib::Device
{
public:
- Device();
+ Device(const synthLib::DeviceCreateParams& _params);
float getSamplerate() const override;
bool isValid() const override;
diff --git a/source/xtLib/xtHardware.cpp b/source/xtLib/xtHardware.cpp
@@ -9,9 +9,16 @@
namespace xt
{
- Hardware::Hardware()
+ Rom initializeRom(const std::vector<uint8_t>& _romData, const std::string& _romName)
+ {
+ if(_romData.empty())
+ return RomLoader::findROM();
+ return Rom{_romName, _romData};
+ }
+
+ Hardware::Hardware(const std::vector<uint8_t>& _romData, const std::string& _romName)
: wLib::Hardware(40000)
- , m_rom(RomLoader::findROM())
+ , m_rom(initializeRom(_romData, _romName))
, m_uc(m_rom)
, m_dsps{DSP(*this, m_uc.getHdi08A().getHdi08(), 0)}
, m_midi(m_uc.getQSM(), 40000)
diff --git a/source/xtLib/xtHardware.h b/source/xtLib/xtHardware.h
@@ -19,8 +19,8 @@ namespace xt
static constexpr uint32_t g_dspCount = 1;
public:
- explicit Hardware();
- virtual ~Hardware();
+ explicit Hardware(const std::vector<uint8_t>& _romData, const std::string& _romName);
+ ~Hardware() override;
void process();
diff --git a/source/xtLib/xtRom.h b/source/xtLib/xtRom.h
@@ -13,7 +13,7 @@ namespace xt
public:
static constexpr uint32_t Size = g_romSize;
- Rom(std::vector<uint8_t> _data) : ROM({}, Size, std::move(_data))
+ Rom(const std::string& _filename, std::vector<uint8_t> _data) : ROM(_filename, Size, std::move(_data))
{
}
@@ -24,7 +24,7 @@ namespace xt
static Rom invalid()
{
- return {{}};
+ return {{}, {}};
}
};
}
diff --git a/source/xtLib/xtRomLoader.cpp b/source/xtLib/xtRomLoader.cpp
@@ -153,7 +153,7 @@ namespace xt
best.name += "_upgraded_" + bestMidi.name;
}
- return {best.data};
+ return {best.name, best.data};
}
std::vector<RomLoader::File> RomLoader::findFiles(const std::string& _extension, const size_t _sizeMin, const size_t _sizeMax)
diff --git a/source/xtLib/xtUc.cpp b/source/xtLib/xtUc.cpp
@@ -17,7 +17,7 @@ namespace xt
if(!_rom.isValid())
return;
- memcpy(m_romRuntimeData.data(), _rom.getData(), g_romSize);
+ memcpy(m_romRuntimeData.data(), _rom.getData().data(), g_romSize);
m_memory.fill(0);
// dumpAssembly("xt_68k.asm", g_romAddr, g_romSize);
diff --git a/source/xtTestConsole/xtTestConsole.cpp b/source/xtTestConsole/xtTestConsole.cpp
@@ -12,7 +12,7 @@ int main()
{
// dsp56k::JitUnittests tests;
- const std::unique_ptr uc(std::make_unique<xt::Xt>());
+ const std::unique_ptr uc(std::make_unique<xt::Xt>(std::vector<uint8_t>(), std::string()));
constexpr uint32_t blockSize = 64;
std::vector<dsp56k::TWord> stereoOutput;