Commit 2dfd1546 by Ben Clayton Committed by Ben Clayton

Initial drop of cppdap

parents
# http://clang.llvm.org/docs/ClangFormatStyleOptions.html
BasedOnStyle: Chromium
\ No newline at end of file
build/
.vs/
.vscode/settings.json
CMakeSettings.json
[submodule "third_party/json"]
path = third_party/json
url = https://github.com/nlohmann/json.git
[submodule "third_party/googletest"]
path = third_party/googletest
url = https://github.com/google/googletest.git
{
"configurations": [
{
"name": "Linux",
"includePath": [
"${workspaceFolder}/**",
"${workspaceFolder}/third_party/json/include",
"${workspaceFolder}/third_party/googletest/googlemock/include"
],
"defines": [],
"compilerPath": "/usr/bin/gcc",
"cStandard": "c11",
"cppStandard": "c++17",
"intelliSenseMode": "clang-x64"
}
],
"version": 4
}
\ No newline at end of file
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "cppdap: hello_debugger",
"type": "hello_debugger",
"request": "launch"
},
{
"type": "lldb",
"request": "launch",
"name": "unittests (lldb)",
"program": "${workspaceFolder}/build/dap-unittests",
"cwd": "${workspaceRoot}",
},
{
"name": "unittests (gdb)",
"type": "cppdbg",
"request": "launch",
"program": "${workspaceFolder}/build/dap-unittests",
"args": [],
"stopAtEntry": false,
"cwd": "${workspaceFolder}",
"environment": [],
"externalConsole": false,
"MIMode": "gdb",
"setupCommands": [
{
"description": "Enable pretty-printing for gdb",
"text": "-enable-pretty-printing",
"ignoreFailures": true
}
]
}
]
}
\ No newline at end of file
{
// See https://go.microsoft.com/fwlink/?LinkId=733558
// for the documentation about the tasks.json format
"version": "2.0.0",
"tasks": [
{
"label": "make",
"group": {
"kind": "build",
"isDefault": true
},
"type": "shell",
"command": "sh",
"osx": {
"args": [
"-c",
"cmake --build . && echo Done"
]
},
"linux": {
"args": [
"-c",
"cmake --build . && echo Done"
]
},
"windows": {
"args": [
"-c",
"cmake --build . && echo Done"
]
},
"options": {
"cwd": "${workspaceRoot}/build",
},
"presentation": {
"echo": false,
"reveal": "always",
"focus": false,
"panel": "shared",
"showReuseMessage": false,
"clear": true,
},
"problemMatcher": {
"owner": "cpp",
"fileLocation": "absolute",
"pattern": {
"regexp": "^(.*):(\\d+):(\\d+):\\s+(warning|error):\\s+(.*)$",
"file": 1,
"line": 2,
"column": 3,
"severity": 4,
"message": 5
}
}
},
{
"label": "cmake",
"type": "shell",
"command": "sh",
"args": [
"-c",
"mkdir build; cd build; cmake .. -GNinja -DCMAKE_BUILD_TYPE=${input:buildType} -DCPPDAP_BUILD_TESTS=1 -DCPPDAP_BUILD_EXAMPLES=1 -DCPPDAP_INSTALL_VSCODE_EXAMPLES=1 -DCPPDAP_WARNINGS_AS_ERRORS=1",
],
"options": {
"cwd": "${workspaceRoot}"
},
"problemMatcher": [],
},
],
"inputs": [
{
"id": "buildType",
"type": "pickString",
"options": [
"Debug",
"Release",
"MinSizeRel",
"RelWithDebInfo",
],
"default": "Debug",
"description": "The type of build",
},
]
}
\ No newline at end of file
# Copyright 2019 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
cmake_minimum_required(VERSION 2.8)
set (CMAKE_CXX_STANDARD 11)
project(cppdap C CXX)
###########################################################
# Options
###########################################################
option(CPPDAP_WARNINGS_AS_ERRORS "Treat warnings as errors" OFF)
option(CPPDAP_BUILD_EXAMPLES "Build example applications" OFF)
option(CPPDAP_BUILD_TESTS "Build tests" OFF)
option(CPPDAP_ASAN "Build dap with address sanitizer" OFF)
option(CPPDAP_MSAN "Build dap with memory sanitizer" OFF)
option(CPPDAP_TSAN "Build dap with thread sanitizer" OFF)
option(CPPDAP_INSTALL_VSCODE_EXAMPLES "Build and install dap examples into vscode extensions directory" OFF)
option(CPPDAP_INSTALL "Create dap install target" OFF)
###########################################################
# Directories
###########################################################
set(CPPDAP_SRC_DIR ${CMAKE_CURRENT_SOURCE_DIR}/src)
set(CPPDAP_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/include)
set(CPPDAP_THIRD_PARTY_DIR ${CMAKE_CURRENT_SOURCE_DIR}/third_party)
set(JSON_DIR ${CPPDAP_THIRD_PARTY_DIR}/json)
set(GOOGLETEST_DIR ${CPPDAP_THIRD_PARTY_DIR}/googletest)
###########################################################
# Submodules
###########################################################
if(CPPDAP_BUILD_TESTS)
if(NOT EXISTS ${CPPDAP_THIRD_PARTY_DIR}/googletest/.git)
message(WARNING "third_party/googletest submodule missing.")
message(WARNING "Run: `git submodule update --init` to build tests.")
set(CPPDAP_BUILD_TESTS OFF)
endif()
endif(CPPDAP_BUILD_TESTS)
###########################################################
# File lists
###########################################################
set(CPPDAP_LIST
${CPPDAP_SRC_DIR}/content_stream.cpp
${CPPDAP_SRC_DIR}/io.cpp
${CPPDAP_SRC_DIR}/json_serializer.cpp
${CPPDAP_SRC_DIR}/network.cpp
${CPPDAP_SRC_DIR}/protocol_events.cpp
${CPPDAP_SRC_DIR}/protocol_requests.cpp
${CPPDAP_SRC_DIR}/protocol_response.cpp
${CPPDAP_SRC_DIR}/protocol_types.cpp
${CPPDAP_SRC_DIR}/session.cpp
${CPPDAP_SRC_DIR}/socket.cpp
${CPPDAP_SRC_DIR}/typeof.cpp
)
###########################################################
# OS libraries
###########################################################
if(CMAKE_SYSTEM_NAME MATCHES "Windows")
set(CPPDAP_OS_LIBS WS2_32)
elseif(CMAKE_SYSTEM_NAME MATCHES "Linux")
set(CPPDAP_OS_LIBS pthread)
elseif(CMAKE_SYSTEM_NAME MATCHES "Darwin")
set(CPPDAP_OS_LIBS)
endif()
###########################################################
# Functions
###########################################################
function(cppdap_set_target_options target)
# Enable all warnings
if(MSVC)
target_compile_options(${target} PRIVATE "-W4")
else()
target_compile_options(${target} PRIVATE "-Wall")
endif()
# Disable specific, pedantic warnings
if(MSVC)
target_compile_options(${target} PRIVATE "-D_CRT_SECURE_NO_WARNINGS")
endif()
# Treat all warnings as errors
if(CPPDAP_WARNINGS_AS_ERRORS)
if(MSVC)
target_compile_options(${target} PRIVATE "/WX")
else()
target_compile_options(${target} PRIVATE "-Werror")
endif()
endif(CPPDAP_WARNINGS_AS_ERRORS)
if(CPPDAP_ASAN)
target_compile_options(${target} PUBLIC "-fsanitize=address")
target_link_libraries(${target} "-fsanitize=address")
elseif(CPPDAP_MSAN)
target_compile_options(${target} PUBLIC "-fsanitize=memory")
target_link_libraries(${target} "-fsanitize=memory")
elseif(CPPDAP_TSAN)
target_compile_options(${target} PUBLIC "-fsanitize=thread")
target_link_libraries(${target} "-fsanitize=thread")
endif()
# Error on undefined symbols
# if(NOT MSVC)
# target_compile_options(${target} PRIVATE "-Wl,--no-undefined")
# endif()
target_include_directories(${target} PRIVATE ${CPPDAP_INCLUDE_DIR})
endfunction(cppdap_set_target_options)
###########################################################
# Targets
###########################################################
# dap
add_library(cppdap STATIC ${CPPDAP_LIST})
set_target_properties(cppdap PROPERTIES
POSITION_INDEPENDENT_CODE 1
)
target_include_directories(cppdap PRIVATE "${JSON_DIR}/include/")
cppdap_set_target_options(cppdap)
target_link_libraries(cppdap "${CPPDAP_OS_LIBS}")
# install
if(CPPDAP_INSTALL)
include(GNUInstallDirs)
install(DIRECTORY ${CPPDAP_INCLUDE_DIR}/cppdap
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
USE_SOURCE_PERMISSIONS
)
install(TARGETS cppdap
EXPORT cppdap-targets
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
)
install(EXPORT cppdap-targets
FILE cppdap-config.cmake
NAMESPACE cppdap::
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/cppdap
)
endif(CPPDAP_INSTALL)
# tests
if(CPPDAP_BUILD_TESTS)
set(DAP_TEST_LIST
${CPPDAP_SRC_DIR}/any_test.cpp
${CPPDAP_SRC_DIR}/chan_test.cpp
${CPPDAP_SRC_DIR}/content_stream_test.cpp
${CPPDAP_SRC_DIR}/dap_test.cpp
${CPPDAP_SRC_DIR}/json_serializer_test.cpp
${CPPDAP_SRC_DIR}/network_test.cpp
${CPPDAP_SRC_DIR}/optional_test.cpp
${CPPDAP_SRC_DIR}/session_test.cpp
${CPPDAP_SRC_DIR}/variant_test.cpp
${GOOGLETEST_DIR}/googletest/src/gtest-all.cc
)
set(DAP_TEST_INCLUDE_DIR
${GOOGLETEST_DIR}/googlemock/include/
${GOOGLETEST_DIR}/googletest/
${GOOGLETEST_DIR}/googletest/include/
${JSON_DIR}/include/
)
add_executable(cppdap-unittests ${DAP_TEST_LIST})
set_target_properties(cppdap-unittests PROPERTIES
INCLUDE_DIRECTORIES "${DAP_TEST_INCLUDE_DIR}"
FOLDER "Tests"
)
if(MSVC)
# googletest emits warning C4244: 'initializing': conversion from 'double' to 'testing::internal::BiggestInt', possible loss of data
target_compile_options(cppdap-unittests PRIVATE "/wd4244")
endif()
cppdap_set_target_options(cppdap-unittests)
target_link_libraries(cppdap-unittests cppdap "${CPPDAP_OS_LIBS}")
endif(CPPDAP_BUILD_TESTS)
# examples
if(CPPDAP_BUILD_EXAMPLES)
function(build_example target)
add_executable(${target} "${CMAKE_CURRENT_SOURCE_DIR}/examples/${target}.cpp")
set_target_properties(${target} PROPERTIES
FOLDER "Examples"
)
cppdap_set_target_options(${target})
target_link_libraries(${target} cppdap "${CPPDAP_OS_LIBS}")
if(CPPDAP_INSTALL_VSCODE_EXAMPLES)
set(extroot "$ENV{HOME}/.vscode/extensions")
if(EXISTS ${extroot})
set(extdir "${extroot}/google.cppdap-example-${target}-1.0.0")
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/examples/vscode/package.json ${extdir}/package.json)
add_custom_command(TARGET ${target}
POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy $<TARGET_FILE:${target}> ${extdir})
else()
message(WARNING "Could not install vscode example extension as '${extroot}' does not exist")
endif()
endif(CPPDAP_INSTALL_VSCODE_EXAMPLES)
endfunction(build_example)
build_example(hello_debugger)
endif(CPPDAP_BUILD_EXAMPLES)
# How to Contribute
We'd love to accept your patches and contributions to this project. There are
just a few small guidelines you need to follow.
## Contributor License Agreement
Contributions to this project must be accompanied by a Contributor License
Agreement. You (or your employer) retain the copyright to your contribution;
this simply gives us permission to use and redistribute your contributions as
part of the project. Head over to <https://cla.developers.google.com/> to see
your current agreements on file or to sign a new one.
You generally only need to submit a CLA once, so if you've already submitted one
(even if it was for a different project), you probably don't need to do it
again.
## Code reviews
All submissions, including submissions by project members, require review. We
use GitHub pull requests for this purpose. Consult
[GitHub Help](https://help.github.com/articles/about-pull-requests/) for more
information on using pull requests.
## Community Guidelines
This project follows
[Google's Open Source Community Guidelines](https://opensource.google/conduct/).
This diff is collapsed. Click to expand it.
# cppdap
## About
`cppdap` is a C++11 library (["SDK"](https://microsoft.github.io/debug-adapter-protocol/implementors/sdks/)) implementation of the [Debug Adapter Protocol](https://microsoft.github.io/debug-adapter-protocol/), providing an API for implementing a DAP client or server.
`cppdap` provides C++ type-safe structures for the full [DAP specification](https://microsoft.github.io/debug-adapter-protocol/specification), and provides a simple way to add custom protocol messages.
## Fetching dependencies
`cppdap` provides CMake build files to build the library, unit tests and examples.
`cppdap` depends on the [`nlohmann/json` library](https://github.com/nlohmann/json), and the unit tests depend on the [`googletest` library](https://github.com/google/googletest). Both are referenced as a git submodules.
Before building, fetch the git submodules with:
```bash
cd <path-to-cppdap>
git submodule update --init
```
## Building
### Linux and macOS
Next, generate the build files:
```bash
cd <path-to-cppdap>
mkdir build
cd build
cmake ..
```
You may wish to suffix the `cmake ..` line with any of the following flags:
* `-DCPPDAP_BUILD_TESTS=1` - Builds the `cppdap` unit tests
* `-DCPPDAP_BUILD_EXAMPLES=1` - Builds the `cppdap` examples
* `-DCPPDAP_INSTALL_VSCODE_EXAMPLES=1` - Installs the `cppdap` examples as Visual Studio Code extensions
* `-DCPPDAP_WARNINGS_AS_ERRORS=1` - Treats all compiler warnings as errors.
Finally, build the project:
`make`
### Windows
`cppdap` can be built using [Visual Studio 2019's CMake integration](https://docs.microsoft.com/en-us/cpp/build/cmake-projects-in-visual-studio?view=vs-2019).
### Using `cppdap` in your CMake project
You can build and link `cppdap` using `add_subdirectory()` in your project's `CMakeLists.txt` file:
```cmake
set(CPPDAP_DIR <path-to-cppdap>) # example <path-to-cppdap>: "${CMAKE_CURRENT_SOURCE_DIR}/third_party/cppdap"
add_subdirectory(${CPPDAP_DIR})
```
This will define the `cppdap` library target, which you can pass to `target_link_libraries()`:
```cmake
target_link_libraries(<target> cppdap) # replace <target> with the name of your project's target
```
You will also want to add the `cppdap` public headers to your project's include search paths so you can `#include` the `cppdap` headers:
```cmake
target_include_directories($<target> PRIVATE "${CPPDAP_DIR}/include") # replace <target> with the name of your project's target
```
---
Note: This is not an officially supported Google product
{
"name": "cppdap-example-@target@",
"displayName": "cppdap example: @target@",
"description": "cppdap example: @target@",
"version": "1.0.0",
"preview": false,
"publisher": "Google LLC",
"author": {
"name": "Google LLC"
},
"license": "SEE LICENSE IN LICENSE.txt",
"engines": {
"vscode": "^1.32.0"
},
"categories": [
"Debuggers"
],
"contributes": {
"debuggers": [
{
"type": "@target@",
"program": "@target@",
"label": "cppdap example: @target@",
"configurationAttributes": {}
}
]
}
}
\ No newline at end of file
// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef dap_any_h
#define dap_any_h
#include "typeinfo.h"
#include <assert.h>
namespace dap {
template <typename T>
struct TypeOf;
// any provides a type-safe container for values of any of dap type (boolean,
// integer, number, array, variant, any, null, dap-structs).
class any {
public:
// constructors
inline any() = default;
inline any(const any& other) noexcept;
inline any(any&& other) noexcept;
template <typename T>
inline any(const T& val);
// destructors
inline ~any();
// replaces the contained value with a null.
inline void reset();
// assignment
inline any& operator=(const any& rhs);
inline any& operator=(any&& rhs) noexcept;
template <typename T>
inline any& operator=(const T& val);
// get() returns the contained value of the type T.
// If the any does not contain a value of type T, then get() will assert.
template <typename T>
inline T& get() const;
// is() returns true iff the contained value is of type T.
template <typename T>
inline bool is() const;
private:
static inline void* alignUp(void* val, size_t alignment);
inline void alloc(size_t size, size_t align);
inline void free();
inline bool isInBuffer(void* ptr) const;
void* value = nullptr;
const TypeInfo* type = nullptr;
void* heap = nullptr; // heap allocation
uint8_t buffer[32]; // or internal allocation
};
inline any::~any() {
reset();
}
template <typename T>
inline any::any(const T& val) {
*this = val;
}
any::any(const any& other) noexcept : type(other.type) {
if (other.value != nullptr) {
alloc(type->size(), type->alignment());
type->copyConstruct(value, other.value);
}
}
any::any(any&& other) noexcept : value(other.value), type(other.type) {
other.value = nullptr;
other.type = nullptr;
}
void any::reset() {
if (value != nullptr) {
type->destruct(value);
free();
}
value = nullptr;
type = nullptr;
}
any& any::operator=(const any& rhs) {
reset();
type = rhs.type;
if (rhs.value != nullptr) {
alloc(type->size(), type->alignment());
type->copyConstruct(value, rhs.value);
}
return *this;
}
any& any::operator=(any&& rhs) noexcept {
value = rhs.value;
type = rhs.type;
rhs.value = nullptr;
rhs.type = nullptr;
return *this;
}
template <typename T>
any& any::operator=(const T& val) {
if (!is<T>()) {
reset();
type = TypeOf<T>::type();
alloc(type->size(), type->alignment());
type->copyConstruct(value, &val);
} else {
*reinterpret_cast<T*>(value) = val;
}
return *this;
}
template <typename T>
T& any::get() const {
assert(is<T>());
return *reinterpret_cast<T*>(value);
}
template <typename T>
bool any::is() const {
return type == TypeOf<T>::type();
}
template <>
inline bool any::is<std::nullptr_t>() const {
return value == nullptr;
}
void* any::alignUp(void* val, size_t alignment) {
auto ptr = reinterpret_cast<uintptr_t>(val);
return reinterpret_cast<void*>(alignment *
((ptr + alignment - 1) / alignment));
}
void any::alloc(size_t size, size_t align) {
assert(value == nullptr);
value = alignUp(buffer, align);
if (isInBuffer(reinterpret_cast<uint8_t*>(value) + size - 1)) {
return;
}
heap = new uint8_t[size + align];
value = alignUp(heap, align);
}
void any::free() {
assert(value != nullptr);
if (heap != nullptr) {
delete[] reinterpret_cast<uint8_t*>(heap);
heap = nullptr;
}
value = nullptr;
}
bool any::isInBuffer(void* ptr) const {
auto addr = reinterpret_cast<uintptr_t>(ptr);
return addr >= reinterpret_cast<uintptr_t>(buffer) &&
addr < reinterpret_cast<uintptr_t>(buffer + sizeof(buffer));
}
} // namespace dap
#endif // dap_any_h
// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef dap_io_h
#define dap_io_h
#include <stddef.h> // size_t
#include <memory> // std::unique_ptr
#include <utility> // std::pair
namespace dap {
class Closable {
public:
virtual ~Closable() = default;
// isOpen() returns true if the stream has not been closed.
virtual bool isOpen() = 0;
// close() closes the stream.
virtual void close() = 0;
};
// Reader is an interface for reading from a byte stream.
class Reader : virtual public Closable {
public:
// read() attempts to read at most n bytes into buffer, returning the number
// of bytes read.
// read() will block until the stream is closed or at least one byte is read.
virtual size_t read(void* buffer, size_t n) = 0;
};
// Writer is an interface for writing to a byte stream.
class Writer : virtual public Closable {
public:
// write() writes n bytes from buffer into the stream.
// Returns true on success, or false if there was an error or the stream was
// closed.
virtual bool write(const void* buffer, size_t n) = 0;
};
// ReaderWriter is an interface that combines the Reader and Writer interfaces.
class ReaderWriter : public Reader, public Writer {
// create() returns a ReaderWriter that delegates the interface methods on to
// the provided Reader and Writer.
// isOpen() returns true if the Reader and Writer both return true for
// isOpen().
// close() closes both the Reader and Writer.
static std::shared_ptr<ReaderWriter> create(const std::shared_ptr<Reader>&,
const std::shared_ptr<Writer>&);
};
// pipe() returns a ReaderWriter where the Writer streams to the Reader.
// Writes are internally buffered.
// Calling close() on either the Reader or Writer will close both ends of the
// stream.
std::shared_ptr<ReaderWriter> pipe();
// file() wraps file with a ReaderWriter.
// If closable is false, then a call to ReaderWriter::close() will not close the
// underlying file.
std::shared_ptr<ReaderWriter> file(FILE* file, bool closable = true);
// file() opens (or creates) the file with the given path.
std::shared_ptr<ReaderWriter> file(const char* path);
// spy() returns a Reader that copies all reads from the Reader r to the Writer
// s, using the given optional prefix.
std::shared_ptr<Reader> spy(const std::shared_ptr<Reader>& r,
const std::shared_ptr<Writer>& s,
const char* prefix = "\n->");
// spy() returns a Writer that copies all writes to the Writer w to the Writer
// s, using the given optional prefix.
std::shared_ptr<Writer> spy(const std::shared_ptr<Writer>& w,
const std::shared_ptr<Writer>& s,
const char* prefix = "\n<-");
// writef writes the printf style string to the writer w.
bool writef(const std::shared_ptr<Writer>& w, const char* msg, ...);
} // namespace dap
#endif // dap_io_h
// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef dap_network_h
#define dap_network_h
#include <functional>
#include <memory>
namespace dap {
class ReaderWriter;
namespace net {
// connect() connects to the given TCP address and port.
std::shared_ptr<ReaderWriter> connect(const char* addr, int port);
// Server implements a basic TCP server.
class Server {
public:
using OnError = std::function<void(const char*)>;
using OnConnect = std::function<void(const std::shared_ptr<ReaderWriter>&)>;
virtual ~Server() = default;
// create() constructs and returns a new Server.
static std::unique_ptr<Server> create();
// start() begins listening for connections on the given port.
// callback will be called for each connection.
// onError will be called for any connection errors.
virtual bool start(int port,
const OnConnect& callback,
const OnError& onError = OnError()) = 0;
// stop() stops listening for connections.
// stop() is implicitly called on destruction.
virtual void stop() = 0;
};
} // namespace net
} // namespace dap
#endif // dap_network_h
// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef dap_optional_h
#define dap_optional_h
#include <assert.h>
#include <type_traits>
namespace dap {
// optional holds an 'optional' contained value.
// This is similar to C++17's std::optional.
template <typename T>
class optional {
template <typename U>
using IsConvertibleToT =
typename std::enable_if<std::is_convertible<U, T>::value>::type;
public:
using value_type = T;
// constructors
inline optional() = default;
inline optional(const optional& other);
inline optional(optional&& other);
template <typename U>
inline optional(const optional<U>& other);
template <typename U>
inline optional(optional<U>&& other);
template <typename U = value_type, typename = IsConvertibleToT<U>>
inline optional(U&& value);
// value() returns the contained value.
// If the optional does not contain a value, then value() will assert.
inline T& value();
inline const T& value() const;
// value() returns the contained value, or defaultValue if the optional does
// not contain a value.
inline T& value(const T& defaultValue);
inline const T& value(const T& defaultValue) const;
// operator bool() returns true if the optional contains a value.
inline explicit operator bool() const noexcept;
// has_value() returns true if the optional contains a value.
inline bool has_value() const;
// assignment
inline optional& operator=(const optional& other);
inline optional& operator=(optional&& other) noexcept;
template <typename U = T, typename = IsConvertibleToT<U>>
inline optional& operator=(U&& value);
template <typename U>
inline optional& operator=(const optional<U>& other);
template <typename U>
inline optional& operator=(optional<U>&& other);
// value access
inline const T* operator->() const;
inline T* operator->();
inline const T& operator*() const;
inline T& operator*();
private:
T val = {};
bool set = false;
};
template <typename T>
optional<T>::optional(const optional& other) : val(other.val), set(other.set) {}
template <typename T>
optional<T>::optional(optional&& other)
: val(std::move(other.val)), set(other.set) {}
template <typename T>
template <typename U>
optional<T>::optional(const optional<U>& other) : set(other.has_value()) {
if (set) {
val = static_cast<T>(other.value());
}
}
template <typename T>
template <typename U>
optional<T>::optional(optional<U>&& other) : set(other.has_value()) {
if (set) {
val = static_cast<T>(std::move(other.value()));
}
}
template <typename T>
template <typename U /*= T*/, typename>
optional<T>::optional(U&& value) : val(std::move(value)), set(true) {}
template <typename T>
T& optional<T>::value() {
assert(set);
return val;
}
template <typename T>
const T& optional<T>::value() const {
assert(set);
return val;
}
template <typename T>
T& optional<T>::value(const T& defaultValue) {
if (!has_value()) {
return defaultValue;
}
return val;
}
template <typename T>
const T& optional<T>::value(const T& defaultValue) const {
if (!has_value()) {
return defaultValue;
}
return val;
}
template <typename T>
optional<T>::operator bool() const noexcept {
return set;
}
template <typename T>
bool optional<T>::has_value() const {
return set;
}
template <typename T>
optional<T>& optional<T>::operator=(const optional& other) {
val = other.val;
set = other.set;
return *this;
}
template <typename T>
optional<T>& optional<T>::operator=(optional&& other) noexcept {
val = std::move(other.val);
set = other.set;
return *this;
}
template <typename T>
template <typename U /* = T */, typename>
optional<T>& optional<T>::operator=(U&& value) {
val = std::move(value);
set = true;
return *this;
}
template <typename T>
template <typename U>
optional<T>& optional<T>::operator=(const optional<U>& other) {
val = other.val;
set = other.set;
return *this;
}
template <typename T>
template <typename U>
optional<T>& optional<T>::operator=(optional<U>&& other) {
val = std::move(other.val);
set = other.set;
return *this;
}
template <typename T>
const T* optional<T>::operator->() const {
assert(set);
return &val;
}
template <typename T>
T* optional<T>::operator->() {
assert(set);
return &val;
}
template <typename T>
const T& optional<T>::operator*() const {
assert(set);
return val;
}
template <typename T>
T& optional<T>::operator*() {
assert(set);
return val;
}
template <class T, class U>
inline bool operator==(const optional<T>& lhs, const optional<U>& rhs) {
if (!lhs.has_value() && !rhs.has_value()) {
return true;
}
if (!lhs.has_value() || !rhs.has_value()) {
return false;
}
return lhs.value() == rhs.value();
}
template <class T, class U>
inline bool operator!=(const optional<T>& lhs, const optional<U>& rhs) {
return !(lhs == rhs);
}
template <class T, class U>
inline bool operator<(const optional<T>& lhs, const optional<U>& rhs) {
if (!rhs.has_value()) {
return false;
}
if (!lhs.has_value()) {
return true;
}
return lhs.value() < rhs.value();
}
template <class T, class U>
inline bool operator<=(const optional<T>& lhs, const optional<U>& rhs) {
if (!lhs.has_value()) {
return true;
}
if (!rhs.has_value()) {
return false;
}
return lhs.value() <= rhs.value();
}
template <class T, class U>
inline bool operator>(const optional<T>& lhs, const optional<U>& rhs) {
if (!lhs.has_value()) {
return false;
}
if (!rhs.has_value()) {
return true;
}
return lhs.value() > rhs.value();
}
template <class T, class U>
inline bool operator>=(const optional<T>& lhs, const optional<U>& rhs) {
if (!rhs.has_value()) {
return true;
}
if (!lhs.has_value()) {
return false;
}
return lhs.value() >= rhs.value();
}
} // namespace dap
#endif // dap_optional_h
// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef dap_serialization_h
#define dap_serialization_h
#include "typeof.h"
#include "types.h"
#include <type_traits>
namespace dap {
// Field describes a single field of a struct.
struct Field {
std::string name; // name of the field
ptrdiff_t offset; // offset of the field to the base of the struct
const TypeInfo* type; // type of the field
};
////////////////////////////////////////////////////////////////////////////////
// Deserializer
////////////////////////////////////////////////////////////////////////////////
// Deserializer is the interface used to decode data from structured storage.
// Methods that return a bool use this to indicate success.
class Deserializer {
public:
// deserialization methods for simple data types.
// If the stored object is not of the correct type, then these function will
// return false.
virtual bool deserialize(boolean*) const = 0;
virtual bool deserialize(integer*) const = 0;
virtual bool deserialize(number*) const = 0;
virtual bool deserialize(string*) const = 0;
virtual bool deserialize(object*) const = 0;
virtual bool deserialize(any*) const = 0;
// count() returns the number of elements in the array object referenced by
// this Deserializer.
virtual size_t count() const = 0;
// array() calls the provided std::function for deserializing each array
// element in the array object referenced by this Deserializer.
virtual bool array(const std::function<bool(Deserializer*)>&) const = 0;
// field() calls the provided std::function for deserializing the field with
// the given name from the struct object referenced by this Deserializer.
virtual bool field(const std::string& name,
const std::function<bool(Deserializer*)>&) const = 0;
// deserialize() delegates to TypeOf<T>::type()->deserialize().
template <typename T,
typename = std::enable_if<TypeOf<T>::has_custom_serialization>>
inline bool deserialize(T*) const;
// deserialize() decodes an array.
template <typename T>
inline bool deserialize(dap::array<T>*) const;
// deserialize() decodes an optional.
template <typename T>
inline bool deserialize(dap::optional<T>*) const;
// deserialize() decodes an variant.
template <typename T0, typename... Types>
inline bool deserialize(dap::variant<T0, Types...>*) const;
// deserialize() decodes a list of fields and stores them into the object.
inline bool deserialize(void* object,
const std::initializer_list<Field>&) const;
// deserialize() decodes the struct field f with the given name.
template <typename T>
inline bool field(const std::string& name, T* f) const;
};
template <typename T, typename>
bool Deserializer::deserialize(T* ptr) const {
return TypeOf<T>::type()->deserialize(this, ptr);
}
template <typename T>
bool Deserializer::deserialize(dap::array<T>* vec) const {
auto n = count();
vec->resize(n);
size_t i = 0;
if (!array([&](Deserializer* d) { return d->deserialize(&(*vec)[i++]); })) {
return false;
}
return true;
}
template <typename T>
bool Deserializer::deserialize(dap::optional<T>* opt) const {
T v;
if (deserialize(&v)) {
*opt = v;
};
return true;
}
template <typename T0, typename... Types>
bool Deserializer::deserialize(dap::variant<T0, Types...>* var) const {
return deserialize(&var->value);
}
bool Deserializer::deserialize(
void* object,
const std::initializer_list<Field>& fields) const {
for (auto const& f : fields) {
if (!field(f.name, [&](Deserializer* d) {
auto ptr = reinterpret_cast<uint8_t*>(object) + f.offset;
return f.type->deserialize(d, ptr);
})) {
return false;
}
}
return true;
}
template <typename T>
bool Deserializer::field(const std::string& name, T* v) const {
return this->field(name,
[&](const Deserializer* d) { return d->deserialize(v); });
}
////////////////////////////////////////////////////////////////////////////////
// Serializer
////////////////////////////////////////////////////////////////////////////////
// Serializer is the interface used to encode data to structured storage.
// A Serializer is associated with a single storage object, whos type and value
// is assigned by a call to serialize().
// If serialize() is called multiple times on the same Serializer instance,
// the last type and value is stored.
// Methods that return a bool use this to indicate success.
class Serializer {
public:
using FieldSerializer = std::function<bool(Serializer*)>;
template <typename T>
using IsFieldSerializer = std::is_convertible<T, FieldSerializer>;
// serialization methods for simple data types.
virtual bool serialize(boolean) = 0;
virtual bool serialize(integer) = 0;
virtual bool serialize(number) = 0;
virtual bool serialize(const string&) = 0;
virtual bool serialize(const object&) = 0;
virtual bool serialize(const any&) = 0;
// array() encodes count array elements to the array object referenced by this
// Serializer. The std::function will be called count times, each time with a
// Serializer that should be used to encode the n'th array element's data.
virtual bool array(size_t count, const std::function<bool(Serializer*)>&) = 0;
// field() encodes a field to the struct object referenced by this Serializer.
// The FieldSerializer will be called with a Serializer used to encode the
// field's data.
virtual bool field(const std::string& name, const FieldSerializer&) = 0;
// remove() deletes the object referenced by this Serializer.
// remove() can be used to serialize optionals with no value assigned.
virtual void remove() = 0;
// serialize() delegates to TypeOf<T>::type()->serialize().
template <typename T,
typename = std::enable_if<TypeOf<T>::has_custom_serialization>>
inline bool serialize(const T&);
// serialize() encodes the given array.
template <typename T>
inline bool serialize(const dap::array<T>&);
// serialize() encodes the given optional.
template <typename T>
inline bool serialize(const dap::optional<T>& v);
// serialize() encodes the given variant.
template <typename T0, typename... Types>
inline bool serialize(const dap::variant<T0, Types...>&);
// serialize() encodes all the provided fields of the given object.
inline bool serialize(const void* object,
const std::initializer_list<Field>&);
// deserialize() encodes the given string.
inline bool serialize(const char* v);
// field() encodes the field with the given name and value.
template <
typename T,
typename = typename std::enable_if<!IsFieldSerializer<T>::value>::type>
inline bool field(const std::string& name, const T& v);
};
template <typename T, typename>
bool Serializer::serialize(const T& object) {
return TypeOf<T>::type()->serialize(this, &object);
}
template <typename T>
bool Serializer::serialize(const dap::array<T>& vec) {
auto it = vec.begin();
return array(vec.size(), [&](Serializer* s) { return s->serialize(*it++); });
}
template <typename T>
bool Serializer::serialize(const dap::optional<T>& opt) {
if (!opt.has_value()) {
remove();
return true;
}
return serialize(opt.value());
}
template <typename T0, typename... Types>
bool Serializer::serialize(const dap::variant<T0, Types...>& var) {
return serialize(var.value);
}
bool Serializer::serialize(const void* object,
const std::initializer_list<Field>& fields) {
for (auto const& f : fields) {
if (!field(f.name, [&](Serializer* d) {
auto ptr = reinterpret_cast<const uint8_t*>(object) + f.offset;
return f.type->serialize(d, ptr);
}))
return false;
}
return true;
}
bool Serializer::serialize(const char* v) {
return serialize(std::string(v));
}
template <typename T, typename>
bool Serializer::field(const std::string& name, const T& v) {
return this->field(name, [&](Serializer* s) { return s->serialize(v); });
}
} // namespace dap
#endif // dap_serialization_h
// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef dap_session_h
#define dap_session_h
#include "io.h"
#include "typeinfo.h"
#include "typeof.h"
#include <functional>
#include <future>
namespace dap {
// Forward declarations
struct Request;
struct Response;
struct Event;
// internal functionality
namespace detail {
template <typename T>
struct traits {
static constexpr bool isRequest = std::is_base_of<dap::Request, T>::value;
static constexpr bool isResponse = std::is_base_of<dap::Response, T>::value;
static constexpr bool isEvent = std::is_base_of<dap::Event, T>::value;
};
// ArgTy<F>::type resolves to the first argument type of the function F.
// F can be a function, static member function, or lambda.
template <typename F>
struct ArgTy {
using type = typename ArgTy<decltype(&F::operator())>::type;
};
template <typename R, typename Arg>
struct ArgTy<R (*)(Arg)> {
using type = typename std::decay<Arg>::type;
};
template <typename R, typename C, typename Arg>
struct ArgTy<R (C::*)(Arg) const> {
using type = typename std::decay<Arg>::type;
};
} // namespace detail
////////////////////////////////////////////////////////////////////////////////
// Error
////////////////////////////////////////////////////////////////////////////////
// Error represents an error message in response to a DAP request.
struct Error {
Error() = default;
Error(const std::string& error);
Error(const char* msg, ...);
// operator bool() returns true if there is an error.
inline operator bool() const { return message.size() > 0; }
std::string message; // empty represents success.
};
////////////////////////////////////////////////////////////////////////////////
// ResponseOrError<T>
////////////////////////////////////////////////////////////////////////////////
// ResponseOrError holds either the response to a DAP request or an error
// message.
template <typename T>
struct ResponseOrError {
using Request = T;
inline ResponseOrError() = default;
inline ResponseOrError(const T& response);
inline ResponseOrError(const Error& error);
inline ResponseOrError(const ResponseOrError& other);
T response;
Error error; // empty represents success.
};
template <typename T>
ResponseOrError<T>::ResponseOrError(const T& response) : response(response) {}
template <typename T>
ResponseOrError<T>::ResponseOrError(const Error& error) : error(error) {}
template <typename T>
ResponseOrError<T>::ResponseOrError(const ResponseOrError& other)
: response(other.response), error(other.error) {}
////////////////////////////////////////////////////////////////////////////////
// Session
////////////////////////////////////////////////////////////////////////////////
// Session implements a DAP client or server endpoint.
// The general usage is as follows:
// (1) Create a session with Session::create().
// (2) Register request and event handlers with registerHandler().
// (3) Optionally register a protocol error handler with onError().
// (3) Bind the session to the remote endpoint with bind().
// (4) Send requests or events with send().
class Session {
template <typename T>
using IsRequest = typename std::enable_if<detail::traits<T>::isRequest>::type;
template <typename T>
using IsEvent = typename std::enable_if<detail::traits<T>::isEvent>::type;
template <typename F>
using ArgTy = typename detail::ArgTy<F>::type;
public:
virtual ~Session() = default;
// ErrorHandler is the type of callback function used for reporting protocol
// errors.
using ErrorHandler = std::function<void(const char*)>;
// create() constructs and returns a new Session.
static std::unique_ptr<Session> create();
// onError() registers a error handler that will be called whenever a protocol
// error is encountered.
// Only one error handler can be bound at any given time, and later calls
// will replace the existing error handler.
virtual void onError(const ErrorHandler&) = 0;
// registerHandler() registers a request handler for a specific request type.
// The function F must have one of the following signatures:
// ResponseOrError<ResponseType>(const RequestType&)
// ResponseType(const RequestType&)
// Error(const RequestType&)
template <typename F, typename RequestType = ArgTy<F>>
inline IsRequest<RequestType> registerHandler(F&& handler);
// registerHandler() registers a event handler for a specific event type.
// The function F must have the following signature:
// void(const EventType&)
template <typename F, typename EventType = ArgTy<F>>
inline IsEvent<EventType> registerHandler(F&& handler);
// registerSentHandler() registers the function F to be called when a response
// of the specific type has been sent.
// The function F must have the following signature:
// void(const ResponseOrError<ResponseType>&)
template <typename F, typename ResponseType = typename ArgTy<F>::Request>
inline void registerSentHandler(F&& handler);
// send() sends the request to the connected endpoint and returns a
// std::future that is assigned the request response or error.
template <typename T, typename = IsRequest<T>>
std::future<ResponseOrError<typename T::Response>> send(const T& request);
// send() sends the event to the connected endpoint.
template <typename T, typename = IsEvent<T>>
void send(const T& event);
// bind() connects this Session to an endpoint.
// bind() can only be called once. Repeated calls will raise an error, but
// otherwise will do nothing.
virtual void bind(const std::shared_ptr<Reader>&,
const std::shared_ptr<Writer>&) = 0;
inline void bind(const std::shared_ptr<ReaderWriter>&);
protected:
using RequestSuccessCallback =
std::function<void(const TypeInfo*, const void*)>;
using RequestErrorCallback =
std::function<void(const TypeInfo*, const Error& message)>;
using GenericResponseHandler = std::function<void(const void*, const Error*)>;
using GenericRequestHandler =
std::function<void(const void* args,
const RequestSuccessCallback& onSuccess,
const RequestErrorCallback& onError)>;
using GenericEventHandler = std::function<void(const void* args)>;
using GenericResponseSentHandler =
std::function<void(const void* response, const Error* error)>;
virtual void registerHandler(const TypeInfo* typeinfo,
const GenericRequestHandler& handler) = 0;
virtual void registerHandler(const TypeInfo* typeinfo,
const GenericEventHandler& handler) = 0;
virtual void registerHandler(const TypeInfo* typeinfo,
const GenericResponseSentHandler& handler) = 0;
virtual bool send(const dap::TypeInfo* typeinfo,
const void* request,
const GenericResponseHandler& responseHandler) = 0;
virtual bool send(const TypeInfo*, const void* event) = 0;
};
template <typename F, typename T>
Session::IsRequest<T> Session::registerHandler(F&& handler) {
using ResponseType = typename T::Response;
auto cb = [handler](const void* args, const RequestSuccessCallback& onSuccess,
const RequestErrorCallback& onError) {
ResponseOrError<ResponseType> res =
handler(*reinterpret_cast<const T*>(args));
if (res.error) {
onError(TypeOf<ResponseType>::type(), res.error);
} else {
onSuccess(TypeOf<ResponseType>::type(), &res.response);
}
};
const TypeInfo* typeinfo = TypeOf<T>::type();
registerHandler(typeinfo, cb);
}
template <typename F, typename T>
Session::IsEvent<T> Session::registerHandler(F&& handler) {
auto cb = [handler](const void* args) {
handler(*reinterpret_cast<const T*>(args));
};
const TypeInfo* typeinfo = TypeOf<T>::type();
registerHandler(typeinfo, cb);
}
template <typename F, typename T>
void Session::registerSentHandler(F&& handler) {
auto cb = [handler](const void* response, const Error* error) {
if (error != nullptr) {
handler(ResponseOrError<T>(*error));
} else {
handler(ResponseOrError<T>(*reinterpret_cast<const T*>(response)));
}
};
const TypeInfo* typeinfo = TypeOf<T>::type();
registerHandler(typeinfo, cb);
}
template <typename T, typename>
std::future<ResponseOrError<typename T::Response>> Session::send(
const T& request) {
using Response = typename T::Response;
auto promise = std::make_shared<std::promise<ResponseOrError<Response>>>();
const TypeInfo* typeinfo = TypeOf<T>::type();
auto sent =
send(typeinfo, &request, [=](const void* result, const Error* error) {
if (error != nullptr) {
promise->set_value(ResponseOrError<Response>(*error));
} else {
promise->set_value(ResponseOrError<Response>(
*reinterpret_cast<const Response*>(result)));
}
});
if (!sent) {
promise->set_value(Error("Failed to send request"));
}
return promise->get_future();
}
template <typename T, typename>
void Session::send(const T& event) {
const TypeInfo* typeinfo = TypeOf<T>::type();
send(typeinfo, &event);
}
void Session::bind(const std::shared_ptr<ReaderWriter>& rw) {
bind(rw, rw);
}
} // namespace dap
#endif // dap_session_h
// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef dap_typeinfo_h
#define dap_typeinfo_h
#include <functional>
#include <string>
namespace dap {
class any;
class Deserializer;
class Serializer;
// The TypeInfo interface provides basic runtime type information about DAP
// types. TypeInfo is used by the serialization system to encode and decode DAP
// requests, responses, events and structs.
struct TypeInfo {
virtual std::string name() const = 0;
virtual size_t size() const = 0;
virtual size_t alignment() const = 0;
virtual void construct(void*) const = 0;
virtual void copyConstruct(void* dst, const void* src) const = 0;
virtual void destruct(void*) const = 0;
virtual bool deserialize(const Deserializer*, void*) const = 0;
virtual bool serialize(Serializer*, const void*) const = 0;
};
} // namespace dap
#endif // dap_typeinfo_h
// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef dap_typeof_h
#define dap_typeof_h
#include "typeinfo.h"
#include "types.h"
#include "serialization.h"
namespace dap {
// BasicTypeInfo is an implementation of the TypeInfo interface for the simple
// template type T.
template <typename T>
struct BasicTypeInfo : public TypeInfo {
BasicTypeInfo(const std::string& name) : name_(name) {}
// TypeInfo compliance
inline std::string name() const { return name_; }
inline size_t size() const { return sizeof(T); }
inline size_t alignment() const { return alignof(T); }
inline void construct(void* ptr) const { new (ptr) T(); }
inline void copyConstruct(void* dst, const void* src) const {
new (dst) T(*reinterpret_cast<const T*>(src));
}
inline void destruct(void* ptr) const { reinterpret_cast<T*>(ptr)->~T(); }
inline bool deserialize(const Deserializer* d, void* ptr) const {
return d->deserialize(reinterpret_cast<T*>(ptr));
}
inline bool serialize(Serializer* s, const void* ptr) const {
return s->serialize(*reinterpret_cast<const T*>(ptr));
}
private:
std::string name_;
};
// TypeOf has a template specialization for each DAP type, each declaring a
// const TypeInfo* type() static member function that describes type T.
template <typename T>
struct TypeOf {};
template <>
struct TypeOf<boolean> {
static const TypeInfo* type();
};
template <>
struct TypeOf<string> {
static const TypeInfo* type();
};
template <>
struct TypeOf<integer> {
static const TypeInfo* type();
};
template <>
struct TypeOf<number> {
static const TypeInfo* type();
};
template <>
struct TypeOf<object> {
static const TypeInfo* type();
};
template <>
struct TypeOf<any> {
static const TypeInfo* type();
};
template <>
struct TypeOf<null> {
static const TypeInfo* type();
};
template <typename T>
struct TypeOf<array<T>> {
static inline const TypeInfo* type() {
static BasicTypeInfo<array<T>> typeinfo("array<" +
TypeOf<T>::type()->name() + ">");
return &typeinfo;
}
};
template <typename T0, typename... Types>
struct TypeOf<variant<T0, Types...>> {
static inline const TypeInfo* type() {
static BasicTypeInfo<variant<T0, Types...>> typeinfo("variant");
return &typeinfo;
}
};
template <typename T>
struct TypeOf<optional<T>> {
static inline const TypeInfo* type() {
static BasicTypeInfo<optional<T>> typeinfo("optional<" +
TypeOf<T>::type()->name() + ">");
return &typeinfo;
}
};
// DAP_OFFSETOF() macro is a generalization of the offsetof() macro defined in
// <cstddef>. It evaluates to the offset of the given field, with fewer
// restrictions than offsetof(). We cast the address '32' and subtract it again,
// because null-dereference is undefined behavior.
#define DAP_OFFSETOF(s, m) \
((int)(size_t) & reinterpret_cast<const volatile char&>((((s*)32)->m)) - 32)
// internal functionality
namespace detail {
template <class T, class M>
M member_type(M T::*);
} // namespace detail
// DAP_TYPEOF() returns the type of the struct (s) member (m).
#define DAP_TYPEOF(s, m) decltype(detail::member_type(&s::m))
// DAP_FIELD() declares a structure field for the DAP_IMPLEMENT_STRUCT_TYPEINFO
// macro.
// FIELD is the name of the struct field.
// NAME is the serialized name of the field, as described by the DAP
// specification.
#define DAP_FIELD(FIELD, NAME) \
dap::Field { \
NAME, DAP_OFFSETOF(StructTy, FIELD), \
TypeOf<DAP_TYPEOF(StructTy, FIELD)>::type(), \
}
// DAP_DECLARE_STRUCT_TYPEINFO() declares a TypeOf<> specialization for STRUCT.
#define DAP_DECLARE_STRUCT_TYPEINFO(STRUCT) \
template <> \
struct TypeOf<STRUCT> { \
static constexpr bool has_custom_serialization = true; \
static const TypeInfo* type(); \
}
// DAP_DECLARE_STRUCT_TYPEINFO() implements the type() member function for the
// TypeOf<> specialization for STRUCT.
// STRUCT is the structure typename.
// NAME is the serialized name of the structure, as described by the DAP
// specification. The variadic (...) parameters should be a repeated list of
// DAP_FIELD()s, one for each field of the struct.
#define DAP_IMPLEMENT_STRUCT_TYPEINFO(STRUCT, NAME, ...) \
const TypeInfo* TypeOf<STRUCT>::type() { \
using StructTy = STRUCT; \
struct TI : BasicTypeInfo<StructTy> { \
TI() : BasicTypeInfo<StructTy>(NAME) {} \
bool deserialize(const Deserializer* d, void* ptr) const override { \
return d->deserialize(ptr, {__VA_ARGS__}); \
} \
bool serialize(Serializer* s, const void* ptr) const override { \
return s->serialize(ptr, {__VA_ARGS__}); \
} \
}; \
static TI typeinfo; \
return &typeinfo; \
}
// DAP_STRUCT_TYPEINFO() is a helper for declaring and implementing a TypeOf<>
// specialization for STRUCT in a single statement.
#define DAP_STRUCT_TYPEINFO(STRUCT, NAME, ...) \
DAP_DECLARE_STRUCT_TYPEINFO(STRUCT); \
DAP_IMPLEMENT_STRUCT_TYPEINFO(STRUCT, NAME, __VA_ARGS__)
} // namespace dap
#endif // dap_typeof_h
// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// This file holds the basic serializable types used by the debug adapter
// protocol.
#ifndef dap_types_h
#define dap_types_h
#include "any.h"
#include "optional.h"
#include "variant.h"
#include <unordered_map>
#include <vector>
namespace dap {
// string is a sequence of characters.
// string defaults to an empty string.
using string = std::string;
// boolean holds a true or false value.
// boolean defaults to false.
class boolean {
public:
inline boolean() : val(false) {}
inline boolean(bool i) : val(i) {}
inline operator bool() const { return val; }
inline boolean& operator=(bool i) {
val = i;
return *this;
}
private:
bool val;
};
// integer holds a whole signed number.
// integer defaults to 0.
class integer {
public:
inline integer() : val(0) {}
inline integer(int i) : val(i) {}
inline operator int() const { return val; }
inline integer& operator=(int i) {
val = i;
return *this;
}
inline integer operator++(int) {
auto copy = *this;
val++;
return copy;
}
private:
int val;
};
// number holds a 64-bit floating point number.
// number defaults to 0.
class number {
public:
inline number() : val(0.0) {}
inline number(double i) : val(i) {}
inline operator double() const { return val; }
inline number& operator=(double i) {
val = i;
return *this;
}
private:
double val;
};
// array is a list of items of type T.
// array defaults to an empty list.
template <typename T>
using array = std::vector<T>;
// object is a map of string to any.
// object defaults to an empty map.
using object = std::unordered_map<string, any>;
// null represents no value.
// null is used by any to check for no-value.
using null = std::nullptr_t;
} // namespace dap
#endif // dap_types_h
// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef dap_variant_h
#define dap_variant_h
#include "any.h"
namespace dap {
// internal functionality
namespace detail {
template <typename T, typename...>
struct TypeIsIn {
static constexpr bool value = false;
};
template <typename T, typename List0, typename... ListN>
struct TypeIsIn<T, List0, ListN...> {
static constexpr bool value =
std::is_same<T, List0>::value || TypeIsIn<T, ListN...>::value;
};
} // namespace detail
// variant represents a type-safe union of DAP types.
// variant can hold a value of any of the template argument types.
// variant defaults to a default-constructed T0.
template <typename T0, typename... Types>
class variant {
public:
// constructors
inline variant();
template <typename T>
inline variant(const T& val);
// assignment
template <typename T>
inline variant& operator=(const T& val);
// get() returns the contained value of the type T.
// If the any does not contain a value of type T, then get() will assert.
template <typename T>
inline T& get() const;
// is() returns true iff the contained value is of type T.
template <typename T>
inline bool is() const;
// accepts() returns true iff the variant accepts values of type T.
template <typename T>
static constexpr bool accepts();
private:
friend class Serializer;
friend class Deserializer;
any value;
};
template <typename T0, typename... Types>
variant<T0, Types...>::variant() : value(T0()) {}
template <typename T0, typename... Types>
template <typename T>
variant<T0, Types...>::variant(const T& value) : value(value) {
static_assert(accepts<T>(), "variant does not accept template type T");
}
template <typename T0, typename... Types>
template <typename T>
variant<T0, Types...>& variant<T0, Types...>::operator=(const T& v) {
static_assert(accepts<T>(), "variant does not accept template type T");
value = v;
return *this;
}
template <typename T0, typename... Types>
template <typename T>
T& variant<T0, Types...>::get() const {
static_assert(accepts<T>(), "variant does not accept template type T");
return value.get<T>();
}
template <typename T0, typename... Types>
template <typename T>
bool variant<T0, Types...>::is() const {
return value.is<T>();
}
template <typename T0, typename... Types>
template <typename T>
constexpr bool variant<T0, Types...>::accepts() {
return detail::TypeIsIn<T, T0, Types...>::value;
}
} // namespace dap
#endif // dap_variant_h
// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "dap/any.h"
#include "dap/typeof.h"
#include "dap/types.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
namespace dap {
struct AnyTestObject {
dap::integer i;
dap::number n;
};
DAP_STRUCT_TYPEINFO(AnyTestObject,
"AnyTestObject",
DAP_FIELD(i, "i"),
DAP_FIELD(n, "n"));
} // namespace dap
TEST(Any, EmptyConstruct) {
dap::any any;
ASSERT_TRUE(any.is<dap::null>());
ASSERT_FALSE(any.is<dap::boolean>());
ASSERT_FALSE(any.is<dap::integer>());
ASSERT_FALSE(any.is<dap::number>());
ASSERT_FALSE(any.is<dap::object>());
ASSERT_FALSE(any.is<dap::array<dap::integer>>());
ASSERT_FALSE(any.is<dap::AnyTestObject>());
}
TEST(Any, Boolean) {
dap::any any(dap::boolean(true));
ASSERT_TRUE(any.is<dap::boolean>());
ASSERT_EQ(any.get<dap::boolean>(), dap::boolean(true));
}
TEST(Any, Integer) {
dap::any any(dap::integer(10));
ASSERT_TRUE(any.is<dap::integer>());
ASSERT_EQ(any.get<dap::integer>(), dap::integer(10));
}
TEST(Any, Number) {
dap::any any(dap::number(123.0f));
ASSERT_TRUE(any.is<dap::number>());
ASSERT_EQ(any.get<dap::number>(), dap::number(123.0f));
}
TEST(Any, Array) {
using array = dap::array<dap::integer>;
dap::any any(array({10, 20, 30}));
ASSERT_TRUE(any.is<array>());
ASSERT_EQ(any.get<array>(), array({10, 20, 30}));
}
TEST(Any, Object) {
dap::object o;
o["one"] = dap::integer(1);
o["two"] = dap::integer(2);
o["three"] = dap::integer(3);
dap::any any(o);
ASSERT_TRUE(any.is<dap::object>());
if (any.is<dap::object>()) {
auto got = any.get<dap::object>();
ASSERT_EQ(got.size(), 3);
ASSERT_EQ(got.count("one"), 1);
ASSERT_EQ(got.count("two"), 1);
ASSERT_EQ(got.count("three"), 1);
ASSERT_TRUE(got["one"].is<dap::integer>());
ASSERT_TRUE(got["two"].is<dap::integer>());
ASSERT_TRUE(got["three"].is<dap::integer>());
ASSERT_EQ(got["one"].get<dap::integer>(), dap::integer(1));
ASSERT_EQ(got["two"].get<dap::integer>(), dap::integer(2));
ASSERT_EQ(got["three"].get<dap::integer>(), dap::integer(3));
}
}
TEST(Any, TestObject) {
dap::any any(dap::AnyTestObject{5, 3.0});
ASSERT_TRUE(any.is<dap::AnyTestObject>());
ASSERT_EQ(any.get<dap::AnyTestObject>().i, 5);
ASSERT_EQ(any.get<dap::AnyTestObject>().n, 3.0);
}
TEST(Any, Assign) {
dap::any any;
any = dap::integer(10);
ASSERT_TRUE(any.is<dap::integer>());
ASSERT_FALSE(any.is<dap::boolean>());
ASSERT_FALSE(any.is<dap::AnyTestObject>());
ASSERT_EQ(any.get<dap::integer>(), dap::integer(10));
any = dap::boolean(true);
ASSERT_FALSE(any.is<dap::integer>());
ASSERT_TRUE(any.is<dap::boolean>());
ASSERT_FALSE(any.is<dap::AnyTestObject>());
ASSERT_EQ(any.get<dap::boolean>(), dap::boolean(true));
any = dap::AnyTestObject{5, 3.0};
ASSERT_FALSE(any.is<dap::integer>());
ASSERT_FALSE(any.is<dap::boolean>());
ASSERT_TRUE(any.is<dap::AnyTestObject>());
ASSERT_EQ(any.get<dap::AnyTestObject>().i, 5);
ASSERT_EQ(any.get<dap::AnyTestObject>().n, 3.0);
}
TEST(Any, Reset) {
dap::any any(dap::integer(10));
ASSERT_TRUE(any.is<dap::integer>());
any.reset();
ASSERT_FALSE(any.is<dap::integer>());
}
// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef dap_chan_h
#define dap_chan_h
#include "dap/optional.h"
#include <condition_variable>
#include <mutex>
#include <queue>
namespace dap {
template <typename T>
struct Chan {
public:
void reset();
void close();
optional<T> take();
void put(T&& in);
void put(const T& in);
private:
bool closed = false;
std::queue<T> queue;
std::condition_variable cv;
std::mutex mutex;
};
template <typename T>
void Chan<T>::reset() {
std::unique_lock<std::mutex> lock(mutex);
queue = {};
closed = false;
}
template <typename T>
void Chan<T>::close() {
std::unique_lock<std::mutex> lock(mutex);
closed = true;
cv.notify_all();
}
template <typename T>
optional<T> Chan<T>::take() {
std::unique_lock<std::mutex> lock(mutex);
cv.wait(lock, [&] { return queue.size() > 0 || closed; });
if (queue.size() == 0) {
return optional<T>();
}
auto out = std::move(queue.front());
queue.pop();
return optional<T>(std::move(out));
}
template <typename T>
void Chan<T>::put(T&& in) {
std::unique_lock<std::mutex> lock(mutex);
auto notify = queue.size() == 0 && !closed;
queue.push(std::move(in));
if (notify) {
cv.notify_all();
}
}
template <typename T>
void Chan<T>::put(const T& in) {
std::unique_lock<std::mutex> lock(mutex);
auto notify = queue.size() == 0 && !closed;
queue.push(in);
if (notify) {
cv.notify_all();
}
}
} // namespace dap
#endif // dap_chan_h
\ No newline at end of file
// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "chan.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include <thread>
TEST(ChanTest, PutTakeClose) {
dap::Chan<int> chan;
auto thread = std::thread([&] {
chan.put(10);
chan.put(20);
chan.put(30);
chan.close();
});
EXPECT_EQ(chan.take(), dap::optional<int>(10));
EXPECT_EQ(chan.take(), dap::optional<int>(20));
EXPECT_EQ(chan.take(), dap::optional<int>(30));
EXPECT_EQ(chan.take(), dap::optional<int>());
thread.join();
}
// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "content_stream.h"
#include "dap/io.h"
#include <string.h> // strlen
#include <algorithm> // std::min
namespace dap {
////////////////////////////////////////////////////////////////////////////////
// ContentReader
////////////////////////////////////////////////////////////////////////////////
ContentReader::ContentReader(const std::shared_ptr<Reader>& reader)
: reader(reader) {}
ContentReader& ContentReader::operator=(ContentReader&& rhs) noexcept {
buf = std::move(rhs.buf);
reader = std::move(rhs.reader);
return *this;
}
bool ContentReader::isOpen() {
return reader ? reader->isOpen() : false;
}
void ContentReader::close() {
if (reader) {
reader->close();
}
}
std::string ContentReader::read() {
// Find Content-Length header prefix
if (!scan("Content-Length:")) {
return "";
}
// Skip whitespace and tabs
while (matchAny(" \t")) {
}
// Parse length
size_t len = 0;
while (true) {
auto c = matchAny("0123456789");
if (c == 0) {
break;
}
len *= 10;
len += size_t(c) - size_t('0');
}
if (len == 0) {
return "";
}
// Expect \r\n\r\n
if (!match("\r\n\r\n")) {
return "";
}
// Read message
if (!buffer(len)) {
return "";
}
std::string out;
out.reserve(len);
for (size_t i = 0; i < len; i++) {
out.push_back(static_cast<char>(buf.front()));
buf.pop_front();
}
return out;
}
bool ContentReader::scan(const uint8_t* seq, size_t len) {
while (buffer(len)) {
if (match(seq, len)) {
return true;
}
buf.pop_front();
}
return false;
}
bool ContentReader::scan(const char* str) {
auto len = strlen(str);
return scan(reinterpret_cast<const uint8_t*>(str), len);
}
bool ContentReader::match(const uint8_t* seq, size_t len) {
if (!buffer(len)) {
return false;
}
auto it = buf.begin();
for (size_t i = 0; i < len; i++, it++) {
if (*it != seq[i]) {
return false;
}
}
for (size_t i = 0; i < len; i++) {
buf.pop_front();
}
return true;
}
bool ContentReader::match(const char* str) {
auto len = strlen(str);
return match(reinterpret_cast<const uint8_t*>(str), len);
}
char ContentReader::matchAny(const char* chars) {
if (!buffer(1)) {
return false;
}
int c = buf.front();
if (auto p = strchr(chars, c)) {
buf.pop_front();
return *p;
}
return 0;
}
bool ContentReader::buffer(size_t bytes) {
if (bytes < buf.size()) {
return true;
}
bytes -= buf.size();
while (bytes > 0) {
uint8_t chunk[256];
auto c = std::min(sizeof(chunk), bytes);
if (reader->read(chunk, c) <= 0) {
return false;
}
for (size_t i = 0; i < c; i++) {
buf.push_back(chunk[i]);
}
bytes -= c;
}
return true;
}
////////////////////////////////////////////////////////////////////////////////
// ContentWriter
////////////////////////////////////////////////////////////////////////////////
ContentWriter::ContentWriter(const std::shared_ptr<Writer>& rhs)
: writer(rhs) {}
ContentWriter& ContentWriter::operator=(ContentWriter&& rhs) noexcept {
writer = std::move(rhs.writer);
return *this;
}
bool ContentWriter::isOpen() {
return writer ? writer->isOpen() : false;
}
void ContentWriter::close() {
if (writer) {
writer->close();
}
}
bool ContentWriter::write(const std::string& msg) const {
auto header =
std::string("Content-Length: ") + std::to_string(msg.size()) + "\r\n\r\n";
return writer->write(header.data(), header.size()) &&
writer->write(msg.data(), msg.size());
}
} // namespace dap
\ No newline at end of file
// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef dap_content_stream_h
#define dap_content_stream_h
#include <deque>
#include <memory>
#include <string>
#include <stdint.h>
namespace dap {
// Forward declarations
class Reader;
class Writer;
class ContentReader {
public:
ContentReader() = default;
ContentReader(const std::shared_ptr<Reader>&);
ContentReader& operator=(ContentReader&&) noexcept;
bool isOpen();
void close();
std::string read();
private:
bool scan(const uint8_t* seq, size_t len);
bool scan(const char* str);
bool match(const uint8_t* seq, size_t len);
bool match(const char* str);
char matchAny(const char* chars);
bool buffer(size_t bytes);
std::shared_ptr<Reader> reader;
std::deque<uint8_t> buf;
};
class ContentWriter {
public:
ContentWriter() = default;
ContentWriter(const std::shared_ptr<Writer>&);
ContentWriter& operator=(ContentWriter&&) noexcept;
bool isOpen();
void close();
bool write(const std::string&) const;
private:
std::shared_ptr<Writer> writer;
};
} // namespace dap
#endif // dap_content_stream_h
// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "content_stream.h"
#include "string_buffer.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
TEST(ContentStreamTest, Write) {
auto sb = dap::StringBuffer::create();
auto ptr = sb.get();
dap::ContentWriter cw(std::move(sb));
cw.write("Content payload number one");
cw.write("Content payload number two");
cw.write("Content payload number three");
ASSERT_EQ(ptr->string(),
"Content-Length: 26\r\n\r\nContent payload number one"
"Content-Length: 26\r\n\r\nContent payload number two"
"Content-Length: 28\r\n\r\nContent payload number three");
}
TEST(ContentStreamTest, Read) {
auto sb = dap::StringBuffer::create();
sb->write("Content-Length: 26\r\n\r\nContent payload number one");
sb->write("some unrecognised garbage");
sb->write("Content-Length: 26\r\n\r\nContent payload number two");
sb->write("some more unrecognised garbage");
sb->write("Content-Length: 28\r\n\r\nContent payload number three");
dap::ContentReader cs(std::move(sb));
ASSERT_EQ(cs.read(), "Content payload number one");
ASSERT_EQ(cs.read(), "Content payload number two");
ASSERT_EQ(cs.read(), "Content payload number three");
ASSERT_EQ(cs.read(), "");
}
// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "gmock/gmock.h"
#include "gtest/gtest.h"
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "dap/io.h"
#include <atomic>
#include <condition_variable>
#include <cstdarg>
#include <cstdio>
#include <deque>
#include <mutex>
namespace {
class Pipe : public dap::ReaderWriter {
public:
// dap::ReaderWriter compliance
bool isOpen() override {
std::unique_lock<std::mutex> lock(mutex);
return !closed;
}
void close() override {
std::unique_lock<std::mutex> lock(mutex);
closed = true;
cv.notify_all();
}
size_t read(void* buffer, size_t bytes) override {
std::unique_lock<std::mutex> lock(mutex);
auto out = reinterpret_cast<uint8_t*>(buffer);
size_t n = 0;
while (true) {
cv.wait(lock, [&] { return closed || data.size() > 0; });
if (closed) {
return n;
}
for (; n < bytes && data.size() > 0; n++) {
out[n] = data.front();
data.pop_front();
}
if (n == bytes) {
return n;
}
}
}
bool write(const void* buffer, size_t bytes) override {
std::unique_lock<std::mutex> lock(mutex);
if (closed) {
return false;
}
if (bytes == 0) {
return true;
}
auto notify = data.size() == 0;
auto src = reinterpret_cast<const uint8_t*>(buffer);
for (size_t i = 0; i < bytes; i++) {
data.emplace_back(src[i]);
}
if (notify) {
cv.notify_all();
}
return true;
}
private:
std::mutex mutex;
std::condition_variable cv;
std::deque<uint8_t> data;
bool closed = false;
};
class RW : public dap::ReaderWriter {
public:
RW(const std::shared_ptr<Reader>& r, const std::shared_ptr<Writer>& w)
: r(r), w(w) {}
// dap::ReaderWriter compliance
bool isOpen() override { return r->isOpen() && w->isOpen(); }
void close() override {
r->close();
w->close();
}
size_t read(void* buffer, size_t n) override { return r->read(buffer, n); }
bool write(const void* buffer, size_t n) override {
return w->write(buffer, n);
}
private:
const std::shared_ptr<dap::Reader> r;
const std::shared_ptr<dap::Writer> w;
};
class File : public dap::ReaderWriter {
public:
File(FILE* f, bool closable) : f(f), closable(closable) {}
~File() { close(); }
// dap::ReaderWriter compliance
bool isOpen() override { return !closed; }
void close() override {
if (closable) {
if (!closed.exchange(true)) {
fclose(f);
}
}
}
size_t read(void* buffer, size_t n) override {
std::unique_lock<std::mutex> lock(readMutex);
auto out = reinterpret_cast<char*>(buffer);
for (size_t i = 0; i < n; i++) {
int c = fgetc(f);
if (c == EOF) {
return i;
}
out[i] = char(c);
}
return n;
// return fread(buffer, 1, n, f);
}
bool write(const void* buffer, size_t n) override {
std::unique_lock<std::mutex> lock(writeMutex);
if (fwrite(buffer, 1, n, f) == n) {
fflush(f);
return true;
}
return false;
}
private:
FILE* const f;
std::mutex readMutex;
std::mutex writeMutex;
std::atomic<bool> closed;
const bool closable;
};
class ReaderSpy : public dap::Reader {
public:
ReaderSpy(const std::shared_ptr<dap::Reader>& r,
const std::shared_ptr<dap::Writer>& s,
const std::string& prefix)
: r(r), s(s), prefix(prefix) {}
// dap::Reader compliance
bool isOpen() override { return r->isOpen(); }
void close() override { r->close(); }
size_t read(void* buffer, size_t n) override {
auto c = r->read(buffer, n);
if (c > 0) {
auto chars = reinterpret_cast<const char*>(buffer);
std::string buf = prefix;
buf.append(chars, chars + c);
s->write(buf.data(), buf.size());
}
return c;
}
private:
const std::shared_ptr<dap::Reader> r;
const std::shared_ptr<dap::Writer> s;
const std::string prefix;
};
class WriterSpy : public dap::Writer {
public:
WriterSpy(const std::shared_ptr<dap::Writer>& w,
const std::shared_ptr<dap::Writer>& s,
const std::string& prefix)
: w(w), s(s), prefix(prefix) {}
// dap::Writer compliance
bool isOpen() override { return w->isOpen(); }
void close() override { w->close(); }
bool write(const void* buffer, size_t n) override {
if (!w->write(buffer, n)) {
return false;
}
auto chars = reinterpret_cast<const char*>(buffer);
std::string buf = prefix;
buf.append(chars, chars + n);
s->write(buf.data(), buf.size());
return true;
}
private:
const std::shared_ptr<dap::Writer> w;
const std::shared_ptr<dap::Writer> s;
const std::string prefix;
};
} // anonymous namespace
namespace dap {
std::shared_ptr<ReaderWriter> ReaderWriter::create(
const std::shared_ptr<Reader>& r,
const std::shared_ptr<Writer>& w) {
return std::make_shared<RW>(r, w);
}
std::shared_ptr<ReaderWriter> pipe() {
return std::make_shared<Pipe>();
}
std::shared_ptr<ReaderWriter> file(FILE* f, bool closable /* = true */) {
return std::make_shared<File>(f, closable);
}
std::shared_ptr<ReaderWriter> file(const char* path) {
if (auto f = fopen(path, "wb")) {
return std::make_shared<File>(f, true);
}
return nullptr;
}
// spy() returns a Reader that copies all reads from the Reader r to the Writer
// s, using the given optional prefix.
std::shared_ptr<Reader> spy(const std::shared_ptr<Reader>& r,
const std::shared_ptr<Writer>& s,
const char* prefix /* = "\n<-" */) {
return std::make_shared<ReaderSpy>(r, s, prefix);
}
// spy() returns a Writer that copies all writes to the Writer w to the Writer
// s, using the given optional prefix.
std::shared_ptr<Writer> spy(const std::shared_ptr<Writer>& w,
const std::shared_ptr<Writer>& s,
const char* prefix /* = "\n->" */) {
return std::make_shared<WriterSpy>(w, s, prefix);
}
bool writef(const std::shared_ptr<Writer>& w, const char* msg, ...) {
char buf[2048];
va_list vararg;
va_start(vararg, msg);
vsnprintf(buf, sizeof(buf), msg, vararg);
va_end(vararg);
return w->write(buf, strlen(buf));
}
} // namespace dap
\ No newline at end of file
// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "json_serializer.h"
#include <nlohmann/json.hpp>
namespace {
struct NullDeserializer : public dap::Deserializer {
static NullDeserializer instance;
bool deserialize(dap::boolean*) const override { return false; }
bool deserialize(dap::integer*) const override { return false; }
bool deserialize(dap::number*) const override { return false; }
bool deserialize(dap::string*) const override { return false; }
bool deserialize(dap::object*) const override { return false; }
bool deserialize(dap::any*) const override { return false; }
size_t count() const override { return 0; }
bool array(const std::function<bool(dap::Deserializer*)>&) const override {
return false;
}
bool field(const std::string&,
const std::function<bool(dap::Deserializer*)>&) const override {
return false;
}
};
NullDeserializer NullDeserializer::instance;
} // anonymous namespace
namespace dap {
namespace json {
Deserializer::Deserializer(const std::string& str)
: json(new nlohmann::json(nlohmann::json::parse(str))), ownsJson(true) {}
Deserializer::Deserializer(const nlohmann::json* json)
: json(json), ownsJson(false) {}
Deserializer::~Deserializer() {
if (ownsJson) {
delete json;
}
}
bool Deserializer::deserialize(dap::boolean* v) const {
if (!json->is_boolean()) {
return false;
}
*v = json->get<bool>();
return true;
}
bool Deserializer::deserialize(dap::integer* v) const {
if (!json->is_number_integer()) {
return false;
}
*v = json->get<int>();
return true;
}
bool Deserializer::deserialize(dap::number* v) const {
if (!json->is_number()) {
return false;
}
*v = json->get<double>();
return true;
}
bool Deserializer::deserialize(dap::string* v) const {
if (!json->is_string()) {
return false;
}
*v = json->get<std::string>();
return true;
}
bool Deserializer::deserialize(dap::object* v) const {
v->reserve(json->size());
for (auto& el : json->items()) {
Deserializer d(&el.value());
dap::any val;
if (!d.deserialize(&val)) {
return false;
}
(*v)[el.key()] = val;
}
return true;
}
bool Deserializer::deserialize(dap::any* v) const {
if (json->is_boolean()) {
*v = dap::boolean(json->get<bool>());
} else if (json->is_number_float()) {
*v = dap::number(json->get<double>());
} else if (json->is_number_integer()) {
*v = dap::integer(json->get<int>());
} else if (json->is_string()) {
*v = json->get<std::string>();
} else if (json->is_null()) {
*v = null();
} else {
return false;
}
return true;
}
size_t Deserializer::count() const {
return json->size();
}
bool Deserializer::array(
const std::function<bool(dap::Deserializer*)>& cb) const {
if (!json->is_array()) {
return false;
}
for (size_t i = 0; i < json->size(); i++) {
Deserializer d(&(*json)[i]);
if (!cb(&d)) {
return false;
}
}
return true;
}
bool Deserializer::field(
const std::string& name,
const std::function<bool(dap::Deserializer*)>& cb) const {
if (!json->is_structured()) {
return false;
}
auto it = json->find(name);
if (it == json->end()) {
return cb(&NullDeserializer::instance);
}
auto obj = *it;
Deserializer d(&obj);
return cb(&d);
}
Serializer::Serializer() : json(new nlohmann::json()), ownsJson(true) {}
Serializer::Serializer(nlohmann::json* json) : json(json), ownsJson(false) {}
Serializer::~Serializer() {
if (ownsJson) {
delete json;
}
}
std::string Serializer::dump() const {
return json->dump();
}
bool Serializer::serialize(dap::boolean v) {
*json = (bool)v;
return true;
}
bool Serializer::serialize(dap::integer v) {
*json = (int)v;
return true;
}
bool Serializer::serialize(dap::number v) {
*json = (double)v;
return true;
}
bool Serializer::serialize(const dap::string& v) {
*json = v;
return true;
}
bool Serializer::serialize(const dap::object& v) {
for (auto& it : v) {
Serializer s(&(*json)[it.first]);
if (!s.serialize(it.second)) {
return false;
}
}
return true;
}
bool Serializer::serialize(const dap::any& v) {
if (v.is<dap::boolean>()) {
*json = (bool)v.get<dap::boolean>();
} else if (v.is<dap::integer>()) {
*json = (int)v.get<dap::integer>();
} else if (v.is<dap::number>()) {
*json = (double)v.get<dap::number>();
} else if (v.is<dap::string>()) {
*json = v.get<dap::string>();
} else if (v.is<dap::null>()) {
} else {
return false;
}
return true;
}
bool Serializer::array(size_t count,
const std::function<bool(dap::Serializer*)>& cb) {
*json = std::vector<int>();
for (size_t i = 0; i < count; i++) {
Serializer s(&(*json)[i]);
if (!cb(&s)) {
return false;
}
}
return true;
}
bool Serializer::field(const std::string& name,
const std::function<bool(dap::Serializer*)>& cb) {
Serializer s(&(*json)[name]);
auto res = cb(&s);
if (s.removed) {
json->erase(name);
}
return res;
}
void Serializer::remove() {
removed = true;
}
} // namespace json
} // namespace dap
// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef dap_json_serializer_h
#define dap_json_serializer_h
#include "dap/protocol.h"
#include "dap/serialization.h"
#include "dap/types.h"
#include <nlohmann/json_fwd.hpp>
namespace dap {
namespace json {
struct Deserializer : public dap::Deserializer {
explicit Deserializer(const std::string&);
~Deserializer();
// dap::Deserializer compliance
bool deserialize(boolean* v) const override;
bool deserialize(integer* v) const override;
bool deserialize(number* v) const override;
bool deserialize(string* v) const override;
bool deserialize(object* v) const override;
bool deserialize(any* v) const override;
size_t count() const override;
bool array(const std::function<bool(dap::Deserializer*)>&) const override;
bool field(const std::string& name,
const std::function<bool(dap::Deserializer*)>&) const override;
// Unhide base overloads
template <typename T>
inline bool field(const std::string& name, T* v) {
return dap::Deserializer::field(name, v);
}
template <typename T,
typename = std::enable_if<TypeOf<T>::has_custom_serialization>>
inline bool deserialize(T* v) const {
return dap::Deserializer::deserialize(v);
}
template <typename T>
inline bool deserialize(dap::array<T>* v) const {
return dap::Deserializer::deserialize(v);
}
template <typename T>
inline bool deserialize(dap::optional<T>* v) const {
return dap::Deserializer::deserialize(v);
}
template <typename T0, typename... Types>
inline bool deserialize(dap::variant<T0, Types...>* v) const {
return dap::Deserializer::deserialize(v);
}
inline bool deserialize(void* o,
const std::initializer_list<Field>& f) const {
return dap::Deserializer::deserialize(o, f);
}
template <typename T>
inline bool field(const std::string& name, T* v) const {
return dap::Deserializer::deserialize(name, v);
}
private:
Deserializer(const nlohmann::json*);
const nlohmann::json* const json;
const bool ownsJson;
};
struct Serializer : public dap::Serializer {
Serializer();
~Serializer();
std::string dump() const;
// dap::Serializer compliance
bool serialize(boolean v) override;
bool serialize(integer v) override;
bool serialize(number v) override;
bool serialize(const string& v) override;
bool serialize(const object& v) override;
bool serialize(const any& v) override;
bool array(size_t count,
const std::function<bool(dap::Serializer*)>&) override;
bool field(const std::string& name, const FieldSerializer&) override;
void remove() override;
// Unhide base overloads
template <
typename T,
typename = typename std::enable_if<!IsFieldSerializer<T>::value>::type>
inline bool field(const std::string& name, const T& v) {
return dap::Serializer::field(name, v);
}
template <typename T,
typename = std::enable_if<TypeOf<T>::has_custom_serialization>>
inline bool serialize(const T& v) {
return dap::Serializer::serialize(v);
}
template <typename T>
inline bool serialize(const dap::array<T>& v) {
return dap::Serializer::serialize(v);
}
template <typename T>
inline bool serialize(const dap::optional<T>& v) {
return dap::Serializer::serialize(v);
}
template <typename T0, typename... Types>
inline bool serialize(const dap::variant<T0, Types...>& v) {
return dap::Serializer::serialize(v);
}
inline bool serialize(const void* o, const std::initializer_list<Field>& f) {
return dap::Serializer::serialize(o, f);
}
inline bool serialize(const char* v) { return dap::Serializer::serialize(v); }
private:
Serializer(nlohmann::json*);
nlohmann::json* const json;
const bool ownsJson;
bool removed = false;
};
} // namespace json
} // namespace dap
#endif // dap_json_serializer_h
\ No newline at end of file
// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "json_serializer.h"
#include "dap/typeinfo.h"
#include "dap/typeof.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include <nlohmann/json.hpp>
namespace dap {
struct JSONInnerTestObject {
integer i;
};
DAP_STRUCT_TYPEINFO(JSONInnerTestObject,
"json-inner-test-object",
DAP_FIELD(i, "i"));
struct JSONTestObject {
boolean b;
integer i;
number n;
array<integer> a;
object o;
string s;
optional<integer> o1;
optional<integer> o2;
JSONInnerTestObject inner;
};
DAP_STRUCT_TYPEINFO(JSONTestObject,
"json-test-object",
DAP_FIELD(b, "b"),
DAP_FIELD(i, "i"),
DAP_FIELD(n, "n"),
DAP_FIELD(a, "a"),
DAP_FIELD(o, "o"),
DAP_FIELD(s, "s"),
DAP_FIELD(o1, "o1"),
DAP_FIELD(o2, "o2"),
DAP_FIELD(inner, "inner"));
TEST(JSONSerializer, Decode) {}
} // namespace dap
TEST(JSONSerializer, SerializeDeserialize) {
dap::JSONTestObject encoded;
encoded.b = true;
encoded.i = 32;
encoded.n = 123.456;
encoded.a = {2, 4, 6, 8};
encoded.o["one"] = dap::integer(1);
encoded.o["two"] = dap::number(2);
encoded.s = "hello world";
encoded.o2 = 42;
encoded.inner.i = 70;
dap::json::Serializer s;
ASSERT_TRUE(s.serialize(encoded));
dap::JSONTestObject decoded;
dap::json::Deserializer d(s.dump());
ASSERT_TRUE(d.deserialize(&decoded));
ASSERT_EQ(encoded.b, decoded.b);
ASSERT_EQ(encoded.i, decoded.i);
ASSERT_EQ(encoded.n, decoded.n);
ASSERT_EQ(encoded.a, decoded.a);
ASSERT_EQ(encoded.o["one"].get<dap::integer>(),
decoded.o["one"].get<dap::integer>());
ASSERT_EQ(encoded.o["two"].get<dap::number>(),
decoded.o["two"].get<dap::number>());
ASSERT_EQ(encoded.s, decoded.s);
ASSERT_EQ(encoded.o2, decoded.o2);
ASSERT_EQ(encoded.inner.i, decoded.inner.i);
}
// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "dap/network.h"
#include "socket.h"
#include <mutex>
#include <string>
#include <thread>
namespace {
class Impl : public dap::net::Server {
public:
Impl() {}
~Impl() { stop(); }
bool start(int port,
const OnConnect& onConnect,
const OnError& onError) override {
std::unique_lock<std::mutex> lock(mutex);
stopWithLock();
socket = std::unique_ptr<dap::Socket>(
new dap::Socket("localhost", std::to_string(port).c_str()));
if (!socket->isOpen()) {
onError("Failed to open socket");
return false;
}
running = true;
thread = std::thread([=] {
do {
if (auto rw = socket->accept()) {
onConnect(rw);
continue;
}
if (!isRunning()) {
onError("Failed to accept connection");
}
} while (false);
});
return true;
}
void stop() override {
std::unique_lock<std::mutex> lock(mutex);
stopWithLock();
}
private:
bool isRunning() {
std::unique_lock<std::mutex> lock(mutex);
return running;
}
void stopWithLock() {
if (running) {
socket->close();
thread.join();
running = false;
}
}
std::mutex mutex;
std::thread thread;
std::unique_ptr<dap::Socket> socket;
bool running = false;
OnError errorHandler;
};
} // anonymous namespace
namespace dap {
namespace net {
std::unique_ptr<Server> Server::create() {
return std::unique_ptr<Server>(new Impl());
}
std::shared_ptr<ReaderWriter> connect(const char* addr, int port) {
return Socket::connect(addr, std::to_string(port).c_str());
}
} // namespace net
} // namespace dap
// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "dap/network.h"
#include "dap/io.h"
#include "chan.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include <chrono>
#include <thread>
namespace {
bool write(const std::shared_ptr<dap::Writer>& w, const std::string& s) {
return w->write(s.data(), s.size()) && w->write("\0", 1);
}
std::string read(const std::shared_ptr<dap::Reader>& r) {
char c;
std::string s;
while (r->read(&c, sizeof(c)) > 0) {
if (c == '\0') {
return s;
}
s += c;
}
return r->isOpen() ? "<read failed>" : "<stream closed>";
}
} // anonymous namespace
TEST(Network, ClientServer) {
const int port = 19021;
dap::Chan<bool> done;
auto server = dap::net::Server::create();
if (!server->start(port,
[&](const std::shared_ptr<dap::ReaderWriter>& rw) {
ASSERT_EQ(read(rw), "client to server");
ASSERT_TRUE(write(rw, "server to client"));
done.put(true);
},
[&](const char* err) {
FAIL() << "Server error: " << err;
})) {
FAIL() << "Couldn't start server";
return;
}
for (int i = 0; i < 10; i++) {
if (auto client = dap::net::connect("localhost", port)) {
ASSERT_TRUE(write(client, "client to server"));
ASSERT_EQ(read(client), "server to client");
break;
}
std::this_thread::sleep_for(std::chrono::seconds(1));
}
done.take();
server.reset();
}
// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "dap/optional.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
TEST(Optional, EmptyConstruct) {
dap::optional<int> opt;
ASSERT_FALSE(opt);
ASSERT_FALSE(opt.has_value());
}
TEST(Optional, ValueConstruct) {
dap::optional<int> opt(0);
ASSERT_TRUE(opt);
ASSERT_TRUE(opt.has_value());
}
TEST(Optional, CopyConstruct) {
dap::optional<int> a(10);
dap::optional<int> b(a);
ASSERT_EQ(a, b);
ASSERT_EQ(b.value(), 10);
}
TEST(Optional, CopyCastConstruct) {
dap::optional<int> a(10);
dap::optional<uint16_t> b(a);
ASSERT_EQ(a, b);
ASSERT_EQ(b.value(), (uint16_t)10);
}
TEST(Optional, MoveConstruct) {
dap::optional<int> a(10);
dap::optional<int> b(std::move(a));
ASSERT_EQ(b.value(), 10);
}
TEST(Optional, MoveCastConstruct) {
dap::optional<int> a(10);
dap::optional<uint16_t> b(std::move(a));
ASSERT_EQ(b.value(), (uint16_t)10);
}
TEST(Optional, AssignValue) {
dap::optional<int> a;
a = 10;
ASSERT_EQ(a.value(), 10);
}
TEST(Optional, AssignOptional) {
dap::optional<int> a;
dap::optional<int> b(10);
a = b;
ASSERT_EQ(a.value(), 10);
}
TEST(Optional, MoveAssignOptional) {
dap::optional<int> a;
dap::optional<int> b(10);
a = std::move(b);
ASSERT_EQ(a.value(), 10);
}
TEST(Optional, StarDeref) {
dap::optional<int> a(10);
ASSERT_EQ(*a, 10);
}
TEST(Optional, StarDerefConst) {
const dap::optional<int> a(10);
ASSERT_EQ(*a, 10);
}
TEST(Optional, ArrowDeref) {
struct S {
int i;
};
dap::optional<S> a(S{10});
ASSERT_EQ(a->i, 10);
}
TEST(Optional, ArrowDerefConst) {
struct S {
int i;
};
const dap::optional<S> a(S{10});
ASSERT_EQ(a->i, 10);
}
TEST(Optional, Value) {
const dap::optional<int> a(10);
ASSERT_EQ(a.value(), 10);
}
TEST(Optional, ValueDefault) {
const dap::optional<int> a;
const dap::optional<int> b(20);
ASSERT_EQ(a.value(10), 10);
ASSERT_EQ(b.value(10), 20);
}
TEST(Optional, CompareLT) {
ASSERT_FALSE(dap::optional<int>(5) < dap::optional<int>(3));
ASSERT_FALSE(dap::optional<int>(5) < dap::optional<int>(5));
ASSERT_TRUE(dap::optional<int>(5) < dap::optional<int>(10));
ASSERT_TRUE(dap::optional<int>() < dap::optional<int>(10));
ASSERT_FALSE(dap::optional<int>() < dap::optional<int>());
}
TEST(Optional, CompareLE) {
ASSERT_FALSE(dap::optional<int>(5) <= dap::optional<int>(3));
ASSERT_TRUE(dap::optional<int>(5) <= dap::optional<int>(5));
ASSERT_TRUE(dap::optional<int>(5) <= dap::optional<int>(10));
ASSERT_TRUE(dap::optional<int>() <= dap::optional<int>(10));
ASSERT_TRUE(dap::optional<int>() <= dap::optional<int>());
}
TEST(Optional, CompareGT) {
ASSERT_TRUE(dap::optional<int>(5) > dap::optional<int>(3));
ASSERT_FALSE(dap::optional<int>(5) > dap::optional<int>(5));
ASSERT_FALSE(dap::optional<int>(5) > dap::optional<int>(10));
ASSERT_FALSE(dap::optional<int>() > dap::optional<int>(10));
ASSERT_FALSE(dap::optional<int>() > dap::optional<int>());
}
TEST(Optional, CompareGE) {
ASSERT_TRUE(dap::optional<int>(5) >= dap::optional<int>(3));
ASSERT_TRUE(dap::optional<int>(5) >= dap::optional<int>(5));
ASSERT_FALSE(dap::optional<int>(5) >= dap::optional<int>(10));
ASSERT_FALSE(dap::optional<int>() >= dap::optional<int>(10));
ASSERT_TRUE(dap::optional<int>() >= dap::optional<int>());
}
TEST(Optional, CompareEQ) {
ASSERT_FALSE(dap::optional<int>(5) == dap::optional<int>(3));
ASSERT_TRUE(dap::optional<int>(5) == dap::optional<int>(5));
ASSERT_FALSE(dap::optional<int>(5) == dap::optional<int>(10));
ASSERT_FALSE(dap::optional<int>() == dap::optional<int>(10));
ASSERT_TRUE(dap::optional<int>() == dap::optional<int>());
}
TEST(Optional, CompareNEQ) {
ASSERT_TRUE(dap::optional<int>(5) != dap::optional<int>(3));
ASSERT_FALSE(dap::optional<int>(5) != dap::optional<int>(5));
ASSERT_TRUE(dap::optional<int>(5) != dap::optional<int>(10));
ASSERT_TRUE(dap::optional<int>() != dap::optional<int>(10));
ASSERT_FALSE(dap::optional<int>() != dap::optional<int>());
}
// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Generated with protocol_gen.go -- do not edit this file.
// go run scripts/protocol_gen/protocol_gen.go
#include "dap/protocol.h"
namespace dap {
BreakpointEvent::BreakpointEvent() = default;
BreakpointEvent::~BreakpointEvent() = default;
DAP_IMPLEMENT_STRUCT_TYPEINFO(BreakpointEvent,
"breakpoint",
DAP_FIELD(breakpoint, "breakpoint"),
DAP_FIELD(reason, "reason"));
CapabilitiesEvent::CapabilitiesEvent() = default;
CapabilitiesEvent::~CapabilitiesEvent() = default;
DAP_IMPLEMENT_STRUCT_TYPEINFO(CapabilitiesEvent,
"capabilities",
DAP_FIELD(capabilities, "capabilities"));
ContinuedEvent::ContinuedEvent() = default;
ContinuedEvent::~ContinuedEvent() = default;
DAP_IMPLEMENT_STRUCT_TYPEINFO(ContinuedEvent,
"continued",
DAP_FIELD(allThreadsContinued,
"allThreadsContinued"),
DAP_FIELD(threadId, "threadId"));
ExitedEvent::ExitedEvent() = default;
ExitedEvent::~ExitedEvent() = default;
DAP_IMPLEMENT_STRUCT_TYPEINFO(ExitedEvent,
"exited",
DAP_FIELD(exitCode, "exitCode"));
InitializedEvent::InitializedEvent() = default;
InitializedEvent::~InitializedEvent() = default;
DAP_IMPLEMENT_STRUCT_TYPEINFO(InitializedEvent, "initialized");
LoadedSourceEvent::LoadedSourceEvent() = default;
LoadedSourceEvent::~LoadedSourceEvent() = default;
DAP_IMPLEMENT_STRUCT_TYPEINFO(LoadedSourceEvent,
"loadedSource",
DAP_FIELD(reason, "reason"),
DAP_FIELD(source, "source"));
ModuleEvent::ModuleEvent() = default;
ModuleEvent::~ModuleEvent() = default;
DAP_IMPLEMENT_STRUCT_TYPEINFO(ModuleEvent,
"module",
DAP_FIELD(module, "module"),
DAP_FIELD(reason, "reason"));
OutputEvent::OutputEvent() = default;
OutputEvent::~OutputEvent() = default;
DAP_IMPLEMENT_STRUCT_TYPEINFO(OutputEvent,
"output",
DAP_FIELD(category, "category"),
DAP_FIELD(column, "column"),
DAP_FIELD(data, "data"),
DAP_FIELD(line, "line"),
DAP_FIELD(output, "output"),
DAP_FIELD(source, "source"),
DAP_FIELD(variablesReference,
"variablesReference"));
ProcessEvent::ProcessEvent() = default;
ProcessEvent::~ProcessEvent() = default;
DAP_IMPLEMENT_STRUCT_TYPEINFO(ProcessEvent,
"process",
DAP_FIELD(isLocalProcess, "isLocalProcess"),
DAP_FIELD(name, "name"),
DAP_FIELD(pointerSize, "pointerSize"),
DAP_FIELD(startMethod, "startMethod"),
DAP_FIELD(systemProcessId, "systemProcessId"));
StoppedEvent::StoppedEvent() = default;
StoppedEvent::~StoppedEvent() = default;
DAP_IMPLEMENT_STRUCT_TYPEINFO(StoppedEvent,
"stopped",
DAP_FIELD(allThreadsStopped, "allThreadsStopped"),
DAP_FIELD(description, "description"),
DAP_FIELD(preserveFocusHint, "preserveFocusHint"),
DAP_FIELD(reason, "reason"),
DAP_FIELD(text, "text"),
DAP_FIELD(threadId, "threadId"));
TerminatedEvent::TerminatedEvent() = default;
TerminatedEvent::~TerminatedEvent() = default;
DAP_IMPLEMENT_STRUCT_TYPEINFO(TerminatedEvent,
"terminated",
DAP_FIELD(restart, "restart"));
ThreadEvent::ThreadEvent() = default;
ThreadEvent::~ThreadEvent() = default;
DAP_IMPLEMENT_STRUCT_TYPEINFO(ThreadEvent,
"thread",
DAP_FIELD(reason, "reason"),
DAP_FIELD(threadId, "threadId"));
} // namespace dap
This diff is collapsed. Click to expand it.
// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "socket.h"
#if defined(_WIN32)
#include <winsock2.h>
#include <ws2tcpip.h>
#else
#include <netdb.h>
#include <netinet/in.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <unistd.h>
#endif
#if defined(_WIN32)
#include <atomic>
namespace {
std::atomic<int> wsaInitCount = {0};
} // anonymous namespace
#else
namespace {
using SOCKET = int;
} // anonymous namespace
#endif
namespace {
constexpr SOCKET InvalidSocket = static_cast<SOCKET>(-1);
} // anonymous namespace
class dap::Socket::Shared : public dap::ReaderWriter {
public:
static void init() {
#if defined(_WIN32)
if (wsaInitCount++ == 0) {
WSADATA winsockData;
(void)WSAStartup(MAKEWORD(2, 2), &winsockData);
}
#endif
}
static void term() {
#if defined(_WIN32)
if (--wsaInitCount == 0) {
WSACleanup();
}
#endif
}
static std::shared_ptr<Shared> create(const char* address, const char* port) {
init();
addrinfo hints = {};
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
hints.ai_flags = AI_PASSIVE;
addrinfo* info = nullptr;
getaddrinfo(address, port, &hints, &info);
if (info) {
auto socket =
::socket(info->ai_family, info->ai_socktype, info->ai_protocol);
return std::make_shared<Shared>(*info, socket);
}
term();
return nullptr;
}
Shared(SOCKET socket) : info({}), sock(socket) {}
Shared(const addrinfo& info, SOCKET socket) : info(info), sock(socket) {}
~Shared() {
close();
term();
}
SOCKET socket() { return sock.load(); }
// dap::ReaderWriter compliance
bool isOpen() {
SOCKET s = socket();
if (s == InvalidSocket) {
return false;
}
char error = 0;
socklen_t len = sizeof(error);
getsockopt(s, SOL_SOCKET, SO_ERROR, &error, &len);
if (error != 0) {
sock.compare_exchange_weak(s, InvalidSocket);
return false;
}
return true;
}
void close() {
SOCKET s = sock.exchange(InvalidSocket);
if (s != InvalidSocket) {
#if defined(_WIN32)
closesocket(s);
#else
::shutdown(s, SHUT_RDWR);
::close(s);
#endif
}
}
size_t read(void* buffer, size_t bytes) {
SOCKET s = socket();
if (s == InvalidSocket) {
return 0;
}
return recv(s, reinterpret_cast<char*>(buffer), static_cast<int>(bytes), 0);
}
bool write(const void* buffer, size_t bytes) {
SOCKET s = socket();
if (s == InvalidSocket) {
return false;
}
if (bytes == 0) {
return true;
}
return ::send(s, reinterpret_cast<const char*>(buffer),
static_cast<int>(bytes), 0) > 0;
}
const addrinfo info;
private:
std::atomic<SOCKET> sock = {InvalidSocket};
};
namespace dap {
Socket::Socket(const char* address, const char* port)
: shared(Shared::create(address, port)) {
if (!shared) {
return;
}
auto socket = shared->socket();
if (bind(socket, shared->info.ai_addr, (int)shared->info.ai_addrlen) != 0) {
shared.reset();
return;
}
if (listen(socket, 1) != 0) {
shared.reset();
return;
}
}
std::shared_ptr<ReaderWriter> Socket::accept() const {
if (shared) {
SOCKET socket = shared->socket();
if (socket != InvalidSocket) {
return std::make_shared<Shared>(::accept(socket, 0, 0));
}
}
return {};
}
bool Socket::isOpen() const {
if (shared) {
return shared->isOpen();
}
return false;
}
void Socket::close() const {
if (shared) {
shared->close();
}
}
std::shared_ptr<ReaderWriter> Socket::connect(const char* address,
const char* port) {
auto shared = Shared::create(address, port);
if (::connect(shared->socket(), shared->info.ai_addr,
(int)shared->info.ai_addrlen) == 0) {
return shared;
}
return {};
}
} // namespace dap
// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef dap_socket_h
#define dap_socket_h
#include "dap/io.h"
#include <atomic>
#include <memory>
namespace dap {
class Socket {
public:
class Shared;
static std::shared_ptr<ReaderWriter> connect(const char* address,
const char* port);
Socket(const char* address, const char* port);
bool isOpen() const;
std::shared_ptr<ReaderWriter> accept() const;
void close() const;
private:
std::shared_ptr<Shared> shared;
};
} // namespace dap
#endif // dap_socket_h
// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef dap_string_buffer_h
#define dap_string_buffer_h
#include "dap/io.h"
#include <algorithm> // std::min
#include <cstring> // memcpy
#include <memory> // std::unique_ptr
#include <string>
namespace dap {
class StringBuffer : public virtual Reader, public virtual Writer {
public:
static inline std::unique_ptr<StringBuffer> create();
inline bool write(const std::string& s);
inline std::string string() const;
// Reader / Writer compilance
inline bool isOpen() override;
inline void close() override;
inline size_t read(void* buffer, size_t bytes) override;
inline bool write(const void* buffer, size_t bytes) override;
private:
std::string str;
bool closed = false;
};
bool StringBuffer::isOpen() {
return !closed;
}
void StringBuffer::close() {
closed = true;
}
std::unique_ptr<StringBuffer> StringBuffer::create() {
return std::unique_ptr<StringBuffer>(new StringBuffer());
}
bool StringBuffer::write(const std::string& s) {
return write(s.data(), s.size());
}
std::string StringBuffer::string() const {
return str;
}
size_t StringBuffer::read(void* buffer, size_t bytes) {
if (closed || bytes == 0 || str.size() == 0) {
return 0;
}
auto len = std::min(bytes, str.size());
memcpy(buffer, str.data(), len);
str = std::string(str.begin() + len, str.end());
return len;
}
bool StringBuffer::write(const void* buffer, size_t bytes) {
if (closed) {
return false;
}
auto chars = reinterpret_cast<const char*>(buffer);
str.append(chars, chars + bytes);
return true;
}
} // namespace dap
#endif // dap_string_buffer_h
// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "dap/typeof.h"
namespace dap {
const TypeInfo* TypeOf<boolean>::type() {
static BasicTypeInfo<boolean> typeinfo("boolean");
return &typeinfo;
}
const TypeInfo* TypeOf<string>::type() {
static BasicTypeInfo<string> typeinfo("string");
return &typeinfo;
}
const TypeInfo* TypeOf<integer>::type() {
static BasicTypeInfo<integer> typeinfo("integer");
return &typeinfo;
}
const TypeInfo* TypeOf<number>::type() {
static BasicTypeInfo<number> typeinfo("number");
return &typeinfo;
}
const TypeInfo* TypeOf<object>::type() {
static BasicTypeInfo<object> typeinfo("object");
return &typeinfo;
}
const TypeInfo* TypeOf<any>::type() {
static BasicTypeInfo<any> typeinfo("any");
return &typeinfo;
}
const TypeInfo* TypeOf<null>::type() {
struct TI : public TypeInfo {
inline std::string name() const { return "null"; }
inline size_t size() const { return sizeof(null); }
inline size_t alignment() const { return alignof(null); }
inline void construct(void* ptr) const { new (ptr) null(); }
inline void copyConstruct(void* dst, const void* src) const {
new (dst) null(*reinterpret_cast<const null*>(src));
}
inline void destruct(void* ptr) const {
reinterpret_cast<null*>(ptr)->~null();
}
inline bool deserialize(const Deserializer*, void*) const { return true; }
inline bool serialize(Serializer*, const void*) const { return true; }
};
static TI typeinfo;
return &typeinfo;
} // namespace dap
} // namespace dap
// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "dap/variant.h"
#include "dap/typeof.h"
#include "dap/types.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
namespace dap {
struct VariantTestObject {
dap::integer i;
dap::number n;
};
DAP_STRUCT_TYPEINFO(VariantTestObject,
"VariantTestObject",
DAP_FIELD(i, "i"),
DAP_FIELD(n, "n"));
} // namespace dap
TEST(Variant, EmptyConstruct) {
dap::variant<dap::integer, dap::boolean, dap::VariantTestObject> variant;
ASSERT_TRUE(variant.is<dap::integer>());
ASSERT_FALSE(variant.is<dap::boolean>());
ASSERT_FALSE(variant.is<dap::VariantTestObject>());
}
TEST(Variant, Boolean) {
dap::variant<dap::integer, dap::boolean, dap::VariantTestObject> variant(
dap::boolean(true));
ASSERT_TRUE(variant.is<dap::boolean>());
ASSERT_EQ(variant.get<dap::boolean>(), dap::boolean(true));
}
TEST(Variant, Integer) {
dap::variant<dap::integer, dap::boolean, dap::VariantTestObject> variant(
dap::integer(10));
ASSERT_TRUE(variant.is<dap::integer>());
ASSERT_EQ(variant.get<dap::integer>(), dap::integer(10));
}
TEST(Variant, TestObject) {
dap::variant<dap::integer, dap::boolean, dap::VariantTestObject> variant(
dap::VariantTestObject{5, 3.0});
ASSERT_TRUE(variant.is<dap::VariantTestObject>());
ASSERT_EQ(variant.get<dap::VariantTestObject>().i, 5);
ASSERT_EQ(variant.get<dap::VariantTestObject>().n, 3.0);
}
TEST(Variant, Assign) {
dap::variant<dap::integer, dap::boolean, dap::VariantTestObject> variant(
dap::integer(10));
variant = dap::integer(10);
ASSERT_TRUE(variant.is<dap::integer>());
ASSERT_FALSE(variant.is<dap::boolean>());
ASSERT_FALSE(variant.is<dap::VariantTestObject>());
ASSERT_EQ(variant.get<dap::integer>(), dap::integer(10));
variant = dap::boolean(true);
ASSERT_FALSE(variant.is<dap::integer>());
ASSERT_TRUE(variant.is<dap::boolean>());
ASSERT_FALSE(variant.is<dap::VariantTestObject>());
ASSERT_EQ(variant.get<dap::boolean>(), dap::boolean(true));
variant = dap::VariantTestObject{5, 3.0};
ASSERT_FALSE(variant.is<dap::integer>());
ASSERT_FALSE(variant.is<dap::boolean>());
ASSERT_TRUE(variant.is<dap::VariantTestObject>());
ASSERT_EQ(variant.get<dap::VariantTestObject>().i, 5);
ASSERT_EQ(variant.get<dap::VariantTestObject>().n, 3.0);
}
TEST(Variant, Accepts) {
using variant =
dap::variant<dap::integer, dap::boolean, dap::VariantTestObject>;
ASSERT_TRUE(variant::accepts<dap::integer>());
ASSERT_TRUE(variant::accepts<dap::boolean>());
ASSERT_TRUE(variant::accepts<dap::VariantTestObject>());
ASSERT_FALSE(variant::accepts<dap::number>());
ASSERT_FALSE(variant::accepts<dap::string>());
}
Subproject commit 0a03480824b4fc7883255dbd2fd8940c9f81e22e
Subproject commit f272ad533d32a40a3b2154a76f1ae9a45eacd6d3
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment