├── .appveyor.yml ├── .clang-format ├── .gitignore ├── .gitmodules ├── .travis.yml ├── CMakeLists.txt ├── LICENSE ├── Makefile ├── README.md ├── doxygen.cfg ├── include └── oscpp │ ├── client.hpp │ ├── detail │ ├── endian.hpp │ ├── host.hpp │ └── stream.hpp │ ├── error.hpp │ ├── print.hpp │ ├── server.hpp │ ├── types.hpp │ └── util.hpp ├── test ├── CMakeLists.txt └── oscpp_autocheck.cpp └── tools ├── clang-format └── mdcode.rb /.appveyor.yml: -------------------------------------------------------------------------------- 1 | version: '1.0.{build}' 2 | 3 | image: Visual Studio 2017 4 | 5 | platform: 6 | - x64 7 | 8 | configuration: 9 | - Release 10 | - Debug 11 | 12 | install: 13 | - set PATH=C:\Ruby22\bin;%PATH% 14 | - git submodule update --init --recursive 15 | 16 | before_build: 17 | - ruby -v 18 | - cmake -G "Visual Studio 15 2017 Win64" . 19 | 20 | build: 21 | project: $(APPVEYOR_BUILD_FOLDER)\$(APPVEYOR_PROJECT_NAME).sln 22 | 23 | test_script: 24 | - cd %APPVEYOR_BUILD_FOLDER% 25 | - ctest --config %CONFIGURATION% --output-on-failure 26 | -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | # BasedOnStyle: LLVM 3 | AccessModifierOffset: -4 4 | AlignAfterOpenBracket: Align 5 | AlignConsecutiveAssignments: false 6 | AlignConsecutiveDeclarations: true 7 | AlignEscapedNewlines: Left 8 | AlignOperands: true 9 | AlignTrailingComments: true 10 | AllowAllParametersOfDeclarationOnNextLine: true 11 | AllowShortBlocksOnASingleLine: false 12 | AllowShortCaseLabelsOnASingleLine: false 13 | AllowShortFunctionsOnASingleLine: None 14 | AllowShortIfStatementsOnASingleLine: false 15 | AllowShortLoopsOnASingleLine: false 16 | AlwaysBreakAfterDefinitionReturnType: None 17 | AlwaysBreakAfterReturnType: None 18 | AlwaysBreakBeforeMultilineStrings: false 19 | AlwaysBreakTemplateDeclarations: MultiLine 20 | BinPackArguments: true 21 | BinPackParameters: true 22 | BraceWrapping: 23 | AfterCaseLabel: true 24 | AfterClass: true 25 | AfterControlStatement: true 26 | AfterEnum: true 27 | AfterFunction: true 28 | AfterNamespace: false 29 | AfterObjCDeclaration: true 30 | AfterStruct: true 31 | AfterUnion: true 32 | AfterExternBlock: false 33 | BeforeCatch: true 34 | BeforeElse: true 35 | IndentBraces: false 36 | SplitEmptyFunction: false 37 | SplitEmptyRecord: false 38 | SplitEmptyNamespace: false 39 | BreakBeforeBinaryOperators: None 40 | BreakBeforeBraces: Custom 41 | BreakBeforeInheritanceComma: false 42 | BreakInheritanceList: BeforeColon 43 | BreakBeforeTernaryOperators: true 44 | BreakConstructorInitializers: BeforeComma 45 | BreakAfterJavaFieldAnnotations: false 46 | BreakStringLiterals: true 47 | ColumnLimit: 80 48 | # CommentPragmas: '^ IWYU pragma:' 49 | CompactNamespaces: true 50 | ConstructorInitializerAllOnOneLineOrOnePerLine: false 51 | ConstructorInitializerIndentWidth: 0 52 | ContinuationIndentWidth: 4 53 | DerivePointerAlignment: false 54 | DisableFormat: false 55 | ExperimentalAutoDetectBinPacking: false 56 | FixNamespaceComments: true 57 | ForEachMacros: 58 | - foreach 59 | - Q_FOREACH 60 | - BOOST_FOREACH 61 | IncludeBlocks: Regroup 62 | IncludeCategories: 63 | - Regex: '^( 4 | 5 | Permission is hereby granted, free of charge, to any person or organization 6 | obtaining a copy of the software and accompanying documentation covered by 7 | this license (the "Software") to use, reproduce, display, distribute, 8 | execute, and transmit the Software, and to prepare derivative works of the 9 | Software, and to permit third-parties to whom the Software is furnished to 10 | do so, all subject to the following: 11 | 12 | The copyright notices in the Software and this entire statement, including 13 | the above license grant, this restriction and the following disclaimer, 14 | must be included in all copies of the Software, in whole or in part, and 15 | all derivative works of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT 20 | SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE 21 | FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, 22 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | DEALINGS IN THE SOFTWARE. 24 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: build clean distclean test v verbose 2 | 3 | NINJA_FLAGS = $(args) 4 | 5 | ifeq (1,$(or $(verbose),$(v))) 6 | NINJA_FLAGS += -v 7 | endif 8 | 9 | build: build/CMakeCache.txt 10 | cd build && ninja $(NINJA_FLAGS) 11 | 12 | v: verbose 13 | 14 | verbose: NINJA_FLAGS += -v 15 | verbose: build 16 | 17 | build/CMakeCache.txt: 18 | mkdir -p build 19 | cd build && cmake -G Ninja .. 20 | 21 | clean: 22 | cd build && ninja clean 23 | 24 | distclean: 25 | rm -rf build 26 | 27 | test: build 28 | cd build && ctest -V 29 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://img.shields.io/travis/kaoskorobase/oscpp.svg?style=flat)](https://travis-ci.org/kaoskorobase/oscpp) 2 | [![Build status](https://ci.appveyor.com/api/projects/status/b7qk7t9mmgnc1n1v?svg=true)](https://ci.appveyor.com/project/kaoskorobase/oscpp) 3 | 4 | **oscpp** is a header-only C++11 library for constructing and parsing 5 | [OpenSoundControl](http://opensoundcontrol.org) packets. Supported platforms 6 | are MacOS X, iOS, Linux, Android and Windows; the code should be easily 7 | portable to any platform with a C++11 compiler. **oscpp** intends to be a 8 | minimal, high-performance solution for working with OSC data. The library 9 | doesn't perform memory allocation (except when throwing exceptions) or other 10 | system calls and is suitable for use in realtime sensitive contexts such as 11 | audio driver callbacks. 12 | 13 | **oscpp** conforms to the [OpenSoundControl 1.0 14 | specification](http://opensoundcontrol.org/spec-1_0). Except for arrays, 15 | non-standard message argument types are currently not supported and there is no 16 | direct support for message address patterns or bundle scheduling; it is up to 17 | the user of the library to implement (a subset of) the semantics according to 18 | the spec. 19 | 20 | ## Installation 21 | 22 | Since **oscpp** only consists of header files, the library doesn't need to be 23 | compiled or installed. Simply put the `include` directory into a location that 24 | is searched by your compiler and you're set. 25 | 26 | ## Usage 27 | 28 | **oscpp** places everything in the `OSCPP` namespace, with the two most 29 | important subnamespaces `Client` for constructing packets and `Server` for 30 | parsing packets. 31 | 32 | First let's have a look at how to build OSC packets in memory: Assuming you 33 | have allocated a buffer you can construct a client packet on the stack and 34 | start filling the buffer with data. When all the data has been written, the 35 | `size` method returns the actual size in bytes of the resulting OSC packet. 36 | 37 | ~~~~cpp 38 | #include 39 | 40 | size_t makePacket(void* buffer, size_t size) 41 | { 42 | // Construct a packet 43 | OSCPP::Client::Packet packet(buffer, size); 44 | packet 45 | // Open a bundle with a timetag 46 | .openBundle(1234ULL) 47 | // Add a message with two arguments and an array with 6 elements; 48 | // for efficiency this needs to be known in advance. 49 | .openMessage("/s_new", 2 + OSCPP::Tags::array(6)) 50 | // Write the arguments 51 | .string("sinesweep") 52 | .int32(2) 53 | .openArray() 54 | .string("start-freq") 55 | .float32(330.0f) 56 | .string("end-freq") 57 | .float32(990.0f) 58 | .string("amp") 59 | .float32(0.4f) 60 | .closeArray() 61 | // Every `open` needs a corresponding `close` 62 | .closeMessage() 63 | // Add another message with one argument 64 | .openMessage("/n_free", 1) 65 | .int32(1) 66 | .closeMessage() 67 | // And nother one 68 | .openMessage("/n_set", 3) 69 | .int32(1) 70 | .string("wobble") 71 | // Numeric arguments are converted automatically 72 | // (see below) 73 | .int32(31) 74 | .closeMessage() 75 | .closeBundle(); 76 | return packet.size(); 77 | } 78 | ~~~~ 79 | 80 | Now given a suitable packet transport (e.g. a UDP socket or an in-memory FIFO, 81 | see below for a dummy implementation), a packet can be constructed and sent as 82 | follows: 83 | 84 | ~~~~cpp 85 | class Transport; 86 | 87 | size_t send(Transport* t, const void* buffer, size_t size); 88 | 89 | void sendPacket(Transport* t, void* buffer, size_t bufferSize) 90 | { 91 | const size_t packetSize = makePacket(buffer, bufferSize); 92 | send(t, buffer, packetSize); 93 | } 94 | ~~~~ 95 | 96 | When parsing data from OSC packets you have to handle the two distinct cases of bundles and messages: 97 | 98 | ~~~~cpp 99 | #include 100 | #include 101 | #include 102 | 103 | void handlePacket(const OSCPP::Server::Packet& packet) 104 | { 105 | if (packet.isBundle()) { 106 | // Convert to bundle 107 | OSCPP::Server::Bundle bundle(packet); 108 | 109 | // Print the time 110 | std::cout << "#bundle " << bundle.time() << std::endl; 111 | 112 | // Get packet stream 113 | OSCPP::Server::PacketStream packets(bundle.packets()); 114 | 115 | // Iterate over all the packets and call handlePacket recursively. 116 | // Cuidado: Might lead to stack overflow! 117 | while (!packets.atEnd()) { 118 | handlePacket(packets.next()); 119 | } 120 | } else { 121 | // Convert to message 122 | OSCPP::Server::Message msg(packet); 123 | 124 | // Get argument stream 125 | OSCPP::Server::ArgStream args(msg.args()); 126 | 127 | // Directly compare message address to string with operator==. 128 | // For handling larger address spaces you could use e.g. a 129 | // dispatch table based on std::unordered_map. 130 | if (msg == "/s_new") { 131 | const char* name = args.string(); 132 | const int32_t id = args.int32(); 133 | std::cout << "/s_new" << " " 134 | << name << " " 135 | << id << " "; 136 | // Get the params array as an ArgStream 137 | OSCPP::Server::ArgStream params(args.array()); 138 | while (!params.atEnd()) { 139 | const char* param = params.string(); 140 | const float value = params.float32(); 141 | std::cout << param << ":" << value << " "; 142 | } 143 | std::cout << std::endl; 144 | } else if (msg == "/n_set") { 145 | const int32_t id = args.int32(); 146 | const char* key = args.string(); 147 | // Numeric arguments are converted automatically 148 | // to float32 (e.g. from int32). 149 | const float value = args.float32(); 150 | std::cout << "/n_set" << " " 151 | << id << " " 152 | << key << " " 153 | << value << std::endl; 154 | } else { 155 | // Simply print unknown messages 156 | std::cout << "Unknown message: " << msg << std::endl; 157 | } 158 | } 159 | } 160 | ~~~~ 161 | 162 | Now we can receive data from a message based transport and pass it to our 163 | packet handling function: 164 | 165 | ~~~~cpp 166 | #include 167 | 168 | const size_t kMaxPacketSize = 8192; 169 | 170 | size_t recv(Transport* t, void* buffer, size_t size); 171 | 172 | void recvPacket(Transport* t) 173 | { 174 | std::array buffer; 175 | size_t size = recv(t, buffer.data(), buffer.size()); 176 | handlePacket(OSCPP::Server::Packet(buffer.data(), size)); 177 | } 178 | ~~~~ 179 | 180 | Here's our code in an example main function: 181 | 182 | ~~~~cpp 183 | #include 184 | #include 185 | 186 | Transport* newTransport(); 187 | 188 | int main(int, char**) 189 | { 190 | std::unique_ptr t(newTransport()); 191 | std::array sendBuffer; 192 | try { 193 | sendPacket(t.get(), sendBuffer.data(), sendBuffer.size()); 194 | recvPacket(t.get()); 195 | } catch (std::exception& e) { 196 | std::cerr << "Exception: " << e.what() << std::endl; 197 | } 198 | return 0; 199 | } 200 | ~~~~ 201 | 202 | Compiling and running the example produces the following output: 203 | 204 | ~~~~ 205 | #bundle 1234 206 | /s_new sinesweep 2 start-freq:330 end-freq:990 amp:0.4 207 | Unknown message: /n_free i:1 208 | /n_set 1 wobble 31 209 | ~~~~ 210 | 211 | ## How to run the example 212 | 213 | You can build and run the example by executing 214 | 215 | ~~~~ 216 | make README 217 | ~~~~ 218 | 219 | You'll need to install the [Haskell Platform](http://www.haskell.org/platform/) 220 | and the [Pandoc](http://johnmacfarlane.net/pandoc/) library: 221 | 222 | ~~~~ 223 | cabal install pandoc 224 | ~~~~ 225 | 226 | ## Appendix: Support code 227 | 228 | Here's the code for a trivial transport that has a single packet buffer: 229 | 230 | ~~~~cpp 231 | #include 232 | 233 | class Transport 234 | { 235 | public: 236 | size_t send(const void* buffer, size_t size) 237 | { 238 | size_t n = std::min(m_buffer.size(), size); 239 | std::memcpy(m_buffer.data(), buffer, n); 240 | m_message = n; 241 | return n; 242 | } 243 | 244 | size_t recv(void* buffer, size_t size) 245 | { 246 | if (m_message > 0) { 247 | size_t n = std::min(m_message, size); 248 | std::memcpy(buffer, m_buffer.data(), n); 249 | m_message = 0; 250 | return n; 251 | } 252 | return 0; 253 | } 254 | 255 | private: 256 | std::array m_buffer; 257 | size_t m_message; 258 | }; 259 | 260 | Transport* newTransport() 261 | { 262 | return new Transport; 263 | } 264 | 265 | size_t send(Transport* t, const void* buffer, size_t size) 266 | { 267 | return t->send(buffer, size); 268 | } 269 | 270 | size_t recv(Transport* t, void* buffer, size_t size) 271 | { 272 | return t->recv(buffer, size); 273 | } 274 | ~~~~ 275 | -------------------------------------------------------------------------------- /doxygen.cfg: -------------------------------------------------------------------------------- 1 | # Doxyfile 1.2.15 2 | 3 | # This file describes the settings to be used by the documentation system 4 | # doxygen (www.doxygen.org) for a project 5 | # 6 | # All text after a hash (#) is considered a comment and will be ignored 7 | # The format is: 8 | # TAG = value [value, ...] 9 | # For lists items can also be appended using: 10 | # TAG += value [value, ...] 11 | # Values that contain spaces should be placed between quotes (" ") 12 | 13 | #--------------------------------------------------------------------------- 14 | # General configuration options 15 | #--------------------------------------------------------------------------- 16 | 17 | # The PROJECT_NAME tag is a single word (or a sequence of words surrounded 18 | # by quotes) that should identify the project. 19 | 20 | PROJECT_NAME = "OSC Template Library" 21 | 22 | # The PROJECT_NUMBER tag can be used to enter a project or revision number. 23 | # This could be handy for archiving the generated documentation or 24 | # if some version control system is used. 25 | 26 | PROJECT_NUMBER = "$Id" 27 | 28 | # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) 29 | # base path where the generated documentation will be put. 30 | # If a relative path is entered, it will be relative to the location 31 | # where doxygen was started. If left blank the current directory will be used. 32 | 33 | OUTPUT_DIRECTORY = doc 34 | 35 | # The OUTPUT_LANGUAGE tag is used to specify the language in which all 36 | # documentation generated by doxygen is written. Doxygen will use this 37 | # information to generate all constant output in the proper language. 38 | # The default language is English, other supported languages are: 39 | # Brazilian, Chinese, Croatian, Czech, Danish, Dutch, Finnish, French, 40 | # German, Greek, Hungarian, Italian, Japanese, Korean, Norwegian, Polish, 41 | # Portuguese, Romanian, Russian, Slovak, Slovene, Spanish and Swedish. 42 | 43 | OUTPUT_LANGUAGE = English 44 | 45 | # If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in 46 | # documentation are documented, even if no documentation was available. 47 | # Private class members and static file members will be hidden unless 48 | # the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES 49 | 50 | EXTRACT_ALL = NO 51 | 52 | # If the EXTRACT_PRIVATE tag is set to YES all private members of a class 53 | # will be included in the documentation. 54 | 55 | EXTRACT_PRIVATE = NO 56 | 57 | # If the EXTRACT_STATIC tag is set to YES all static members of a file 58 | # will be included in the documentation. 59 | 60 | EXTRACT_STATIC = NO 61 | 62 | # If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) 63 | # defined locally in source files will be included in the documentation. 64 | # If set to NO only classes defined in header files are included. 65 | 66 | EXTRACT_LOCAL_CLASSES = YES 67 | 68 | # If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all 69 | # undocumented members of documented classes, files or namespaces. 70 | # If set to NO (the default) these members will be included in the 71 | # various overviews, but no documentation section is generated. 72 | # This option has no effect if EXTRACT_ALL is enabled. 73 | 74 | HIDE_UNDOC_MEMBERS = YES 75 | 76 | # If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all 77 | # undocumented classes that are normally visible in the class hierarchy. 78 | # If set to NO (the default) these class will be included in the various 79 | # overviews. This option has no effect if EXTRACT_ALL is enabled. 80 | 81 | HIDE_UNDOC_CLASSES = YES 82 | 83 | # If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will 84 | # include brief member descriptions after the members that are listed in 85 | # the file and class documentation (similar to JavaDoc). 86 | # Set to NO to disable this. 87 | 88 | BRIEF_MEMBER_DESC = YES 89 | 90 | # If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend 91 | # the brief description of a member or function before the detailed description. 92 | # Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the 93 | # brief descriptions will be completely suppressed. 94 | 95 | REPEAT_BRIEF = YES 96 | 97 | # If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then 98 | # Doxygen will generate a detailed section even if there is only a brief 99 | # description. 100 | 101 | ALWAYS_DETAILED_SEC = NO 102 | 103 | # If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all inherited 104 | # members of a class in the documentation of that class as if those members were 105 | # ordinary class members. Constructors, destructors and assignment operators of 106 | # the base classes will not be shown. 107 | 108 | INLINE_INHERITED_MEMB = NO 109 | 110 | # If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full 111 | # path before files name in the file list and in the header files. If set 112 | # to NO the shortest path that makes the file name unique will be used. 113 | 114 | FULL_PATH_NAMES = NO 115 | 116 | # If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag 117 | # can be used to strip a user defined part of the path. Stripping is 118 | # only done if one of the specified strings matches the left-hand part of 119 | # the path. It is allowed to use relative paths in the argument list. 120 | 121 | STRIP_FROM_PATH = 122 | 123 | # The INTERNAL_DOCS tag determines if documentation 124 | # that is typed after a \internal command is included. If the tag is set 125 | # to NO (the default) then the documentation will be excluded. 126 | # Set it to YES to include the internal documentation. 127 | 128 | INTERNAL_DOCS = NO 129 | 130 | # Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct 131 | # doxygen to hide any special comment blocks from generated source code 132 | # fragments. Normal C and C++ comments will always remain visible. 133 | 134 | STRIP_CODE_COMMENTS = YES 135 | 136 | # If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate 137 | # file names in lower case letters. If set to YES upper case letters are also 138 | # allowed. This is useful if you have classes or files whose names only differ 139 | # in case and if your file system supports case sensitive file names. Windows 140 | # users are adviced to set this option to NO. 141 | 142 | CASE_SENSE_NAMES = YES 143 | 144 | # If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter 145 | # (but less readable) file names. This can be useful is your file systems 146 | # doesn't support long names like on DOS, Mac, or CD-ROM. 147 | 148 | SHORT_NAMES = NO 149 | 150 | # If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen 151 | # will show members with their full class and namespace scopes in the 152 | # documentation. If set to YES the scope will be hidden. 153 | 154 | HIDE_SCOPE_NAMES = NO 155 | 156 | # If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen 157 | # will generate a verbatim copy of the header file for each class for 158 | # which an include is specified. Set to NO to disable this. 159 | 160 | VERBATIM_HEADERS = YES 161 | 162 | # If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen 163 | # will put list of the files that are included by a file in the documentation 164 | # of that file. 165 | 166 | SHOW_INCLUDE_FILES = YES 167 | 168 | # If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen 169 | # will interpret the first line (until the first dot) of a JavaDoc-style 170 | # comment as the brief description. If set to NO, the JavaDoc 171 | # comments will behave just like the Qt-style comments (thus requiring an 172 | # explict @brief command for a brief description. 173 | 174 | JAVADOC_AUTOBRIEF = NO 175 | 176 | # If the INHERIT_DOCS tag is set to YES (the default) then an undocumented 177 | # member inherits the documentation from any documented member that it 178 | # reimplements. 179 | 180 | INHERIT_DOCS = YES 181 | 182 | # If the INLINE_INFO tag is set to YES (the default) then a tag [inline] 183 | # is inserted in the documentation for inline members. 184 | 185 | INLINE_INFO = YES 186 | 187 | # If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen 188 | # will sort the (detailed) documentation of file and class members 189 | # alphabetically by member name. If set to NO the members will appear in 190 | # declaration order. 191 | 192 | SORT_MEMBER_DOCS = YES 193 | 194 | # If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC 195 | # tag is set to YES, then doxygen will reuse the documentation of the first 196 | # member in the group (if any) for the other members of the group. By default 197 | # all members of a group must be documented explicitly. 198 | 199 | DISTRIBUTE_GROUP_DOC = NO 200 | 201 | # The TAB_SIZE tag can be used to set the number of spaces in a tab. 202 | # Doxygen uses this value to replace tabs by spaces in code fragments. 203 | 204 | TAB_SIZE = 8 205 | 206 | # The GENERATE_TODOLIST tag can be used to enable (YES) or 207 | # disable (NO) the todo list. This list is created by putting \todo 208 | # commands in the documentation. 209 | 210 | GENERATE_TODOLIST = YES 211 | 212 | # The GENERATE_TESTLIST tag can be used to enable (YES) or 213 | # disable (NO) the test list. This list is created by putting \test 214 | # commands in the documentation. 215 | 216 | GENERATE_TESTLIST = YES 217 | 218 | # The GENERATE_BUGLIST tag can be used to enable (YES) or 219 | # disable (NO) the bug list. This list is created by putting \bug 220 | # commands in the documentation. 221 | 222 | GENERATE_BUGLIST = YES 223 | 224 | # This tag can be used to specify a number of aliases that acts 225 | # as commands in the documentation. An alias has the form "name=value". 226 | # For example adding "sideeffect=\par Side Effects:\n" will allow you to 227 | # put the command \sideeffect (or @sideeffect) in the documentation, which 228 | # will result in a user defined paragraph with heading "Side Effects:". 229 | # You can put \n's in the value part of an alias to insert newlines. 230 | 231 | ALIASES = 232 | 233 | # The ENABLED_SECTIONS tag can be used to enable conditional 234 | # documentation sections, marked by \if sectionname ... \endif. 235 | 236 | ENABLED_SECTIONS = 237 | 238 | # The MAX_INITIALIZER_LINES tag determines the maximum number of lines 239 | # the initial value of a variable or define consist of for it to appear in 240 | # the documentation. If the initializer consists of more lines than specified 241 | # here it will be hidden. Use a value of 0 to hide initializers completely. 242 | # The appearance of the initializer of individual variables and defines in the 243 | # documentation can be controlled using \showinitializer or \hideinitializer 244 | # command in the documentation regardless of this setting. 245 | 246 | MAX_INITIALIZER_LINES = 30 247 | 248 | # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources 249 | # only. Doxygen will then generate output that is more tailored for C. 250 | # For instance some of the names that are used will be different. The list 251 | # of all members will be omitted, etc. 252 | 253 | OPTIMIZE_OUTPUT_FOR_C = NO 254 | 255 | # Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java sources 256 | # only. Doxygen will then generate output that is more tailored for Java. 257 | # For instance namespaces will be presented as packages, qualified scopes 258 | # will look different, etc. 259 | 260 | OPTIMIZE_OUTPUT_JAVA = NO 261 | 262 | # Set the SHOW_USED_FILES tag to NO to disable the list of files generated 263 | # at the bottom of the documentation of classes and structs. If set to YES the 264 | # list will mention the files that were used to generate the documentation. 265 | 266 | SHOW_USED_FILES = YES 267 | 268 | #--------------------------------------------------------------------------- 269 | # configuration options related to warning and progress messages 270 | #--------------------------------------------------------------------------- 271 | 272 | # The QUIET tag can be used to turn on/off the messages that are generated 273 | # by doxygen. Possible values are YES and NO. If left blank NO is used. 274 | 275 | QUIET = NO 276 | 277 | # The WARNINGS tag can be used to turn on/off the warning messages that are 278 | # generated by doxygen. Possible values are YES and NO. If left blank 279 | # NO is used. 280 | 281 | WARNINGS = YES 282 | 283 | # If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings 284 | # for undocumented members. If EXTRACT_ALL is set to YES then this flag will 285 | # automatically be disabled. 286 | 287 | WARN_IF_UNDOCUMENTED = YES 288 | 289 | # The WARN_FORMAT tag determines the format of the warning messages that 290 | # doxygen can produce. The string should contain the $file, $line, and $text 291 | # tags, which will be replaced by the file and line number from which the 292 | # warning originated and the warning text. 293 | 294 | WARN_FORMAT = "$file:$line: $text" 295 | 296 | # The WARN_LOGFILE tag can be used to specify a file to which warning 297 | # and error messages should be written. If left blank the output is written 298 | # to stderr. 299 | 300 | WARN_LOGFILE = 301 | 302 | #--------------------------------------------------------------------------- 303 | # configuration options related to the input files 304 | #--------------------------------------------------------------------------- 305 | 306 | # The INPUT tag can be used to specify the files and/or directories that contain 307 | # documented source files. You may enter file names like "myfile.cpp" or 308 | # directories like "/usr/src/myproject". Separate the files or directories 309 | # with spaces. 310 | 311 | INPUT = . 312 | 313 | # If the value of the INPUT tag contains directories, you can use the 314 | # FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp 315 | # and *.h) to filter out the source-files in the directories. If left 316 | # blank the following patterns are tested: 317 | # *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx *.hpp 318 | # *.h++ *.idl *.odl 319 | 320 | FILE_PATTERNS = *.hh 321 | 322 | # The RECURSIVE tag can be used to turn specify whether or not subdirectories 323 | # should be searched for input files as well. Possible values are YES and NO. 324 | # If left blank NO is used. 325 | 326 | RECURSIVE = NO 327 | 328 | # The EXCLUDE tag can be used to specify files and/or directories that should 329 | # excluded from the INPUT source files. This way you can easily exclude a 330 | # subdirectory from a directory tree whose root is specified with the INPUT tag. 331 | 332 | EXCLUDE = 333 | 334 | # The EXCLUDE_SYMLINKS tag can be used select whether or not files or directories 335 | # that are symbolic links (a Unix filesystem feature) are excluded from the input. 336 | 337 | EXCLUDE_SYMLINKS = NO 338 | 339 | # If the value of the INPUT tag contains directories, you can use the 340 | # EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude 341 | # certain files from those directories. 342 | 343 | EXCLUDE_PATTERNS = 344 | 345 | # The EXAMPLE_PATH tag can be used to specify one or more files or 346 | # directories that contain example code fragments that are included (see 347 | # the \include command). 348 | 349 | EXAMPLE_PATH = 350 | 351 | # If the value of the EXAMPLE_PATH tag contains directories, you can use the 352 | # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp 353 | # and *.h) to filter out the source-files in the directories. If left 354 | # blank all files are included. 355 | 356 | EXAMPLE_PATTERNS = 357 | 358 | # If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be 359 | # searched for input files to be used with the \include or \dontinclude 360 | # commands irrespective of the value of the RECURSIVE tag. 361 | # Possible values are YES and NO. If left blank NO is used. 362 | 363 | EXAMPLE_RECURSIVE = NO 364 | 365 | # The IMAGE_PATH tag can be used to specify one or more files or 366 | # directories that contain image that are included in the documentation (see 367 | # the \image command). 368 | 369 | IMAGE_PATH = 370 | 371 | # The INPUT_FILTER tag can be used to specify a program that doxygen should 372 | # invoke to filter for each input file. Doxygen will invoke the filter program 373 | # by executing (via popen()) the command , where 374 | # is the value of the INPUT_FILTER tag, and is the name of an 375 | # input file. Doxygen will then use the output that the filter program writes 376 | # to standard output. 377 | 378 | INPUT_FILTER = 379 | 380 | # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using 381 | # INPUT_FILTER) will be used to filter the input files when producing source 382 | # files to browse. 383 | 384 | FILTER_SOURCE_FILES = NO 385 | 386 | #--------------------------------------------------------------------------- 387 | # configuration options related to source browsing 388 | #--------------------------------------------------------------------------- 389 | 390 | # If the SOURCE_BROWSER tag is set to YES then a list of source files will 391 | # be generated. Documented entities will be cross-referenced with these sources. 392 | 393 | SOURCE_BROWSER = NO 394 | 395 | # Setting the INLINE_SOURCES tag to YES will include the body 396 | # of functions and classes directly in the documentation. 397 | 398 | INLINE_SOURCES = NO 399 | 400 | # If the REFERENCED_BY_RELATION tag is set to YES (the default) 401 | # then for each documented function all documented 402 | # functions referencing it will be listed. 403 | 404 | REFERENCED_BY_RELATION = YES 405 | 406 | # If the REFERENCES_RELATION tag is set to YES (the default) 407 | # then for each documented function all documented entities 408 | # called/used by that function will be listed. 409 | 410 | REFERENCES_RELATION = YES 411 | 412 | #--------------------------------------------------------------------------- 413 | # configuration options related to the alphabetical class index 414 | #--------------------------------------------------------------------------- 415 | 416 | # If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index 417 | # of all compounds will be generated. Enable this if the project 418 | # contains a lot of classes, structs, unions or interfaces. 419 | 420 | ALPHABETICAL_INDEX = NO 421 | 422 | # If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then 423 | # the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns 424 | # in which this list will be split (can be a number in the range [1..20]) 425 | 426 | COLS_IN_ALPHA_INDEX = 5 427 | 428 | # In case all classes in a project start with a common prefix, all 429 | # classes will be put under the same header in the alphabetical index. 430 | # The IGNORE_PREFIX tag can be used to specify one or more prefixes that 431 | # should be ignored while generating the index headers. 432 | 433 | IGNORE_PREFIX = 434 | 435 | #--------------------------------------------------------------------------- 436 | # configuration options related to the HTML output 437 | #--------------------------------------------------------------------------- 438 | 439 | # If the GENERATE_HTML tag is set to YES (the default) Doxygen will 440 | # generate HTML output. 441 | 442 | GENERATE_HTML = YES 443 | 444 | # The HTML_OUTPUT tag is used to specify where the HTML docs will be put. 445 | # If a relative path is entered the value of OUTPUT_DIRECTORY will be 446 | # put in front of it. If left blank `html' will be used as the default path. 447 | 448 | HTML_OUTPUT = html 449 | 450 | # The HTML_FILE_EXTENSION tag can be used to specify the file extension for 451 | # each generated HTML page (for example: .htm,.php,.asp). If it is left blank 452 | # doxygen will generate files with .html extension. 453 | 454 | HTML_FILE_EXTENSION = .html 455 | 456 | # The HTML_HEADER tag can be used to specify a personal HTML header for 457 | # each generated HTML page. If it is left blank doxygen will generate a 458 | # standard header. 459 | 460 | HTML_HEADER = 461 | 462 | # The HTML_FOOTER tag can be used to specify a personal HTML footer for 463 | # each generated HTML page. If it is left blank doxygen will generate a 464 | # standard footer. 465 | 466 | HTML_FOOTER = 467 | 468 | # The HTML_STYLESHEET tag can be used to specify a user defined cascading 469 | # style sheet that is used by each HTML page. It can be used to 470 | # fine-tune the look of the HTML output. If the tag is left blank doxygen 471 | # will generate a default style sheet 472 | 473 | HTML_STYLESHEET = 474 | 475 | # If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, 476 | # files or namespaces will be aligned in HTML using tables. If set to 477 | # NO a bullet list will be used. 478 | 479 | HTML_ALIGN_MEMBERS = YES 480 | 481 | # If the GENERATE_HTMLHELP tag is set to YES, additional index files 482 | # will be generated that can be used as input for tools like the 483 | # Microsoft HTML help workshop to generate a compressed HTML help file (.chm) 484 | # of the generated HTML documentation. 485 | 486 | GENERATE_HTMLHELP = NO 487 | 488 | # If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag 489 | # controls if a separate .chi index file is generated (YES) or that 490 | # it should be included in the master .chm file (NO). 491 | 492 | GENERATE_CHI = NO 493 | 494 | # If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag 495 | # controls whether a binary table of contents is generated (YES) or a 496 | # normal table of contents (NO) in the .chm file. 497 | 498 | BINARY_TOC = NO 499 | 500 | # The TOC_EXPAND flag can be set to YES to add extra items for group members 501 | # to the contents of the Html help documentation and to the tree view. 502 | 503 | TOC_EXPAND = NO 504 | 505 | # The DISABLE_INDEX tag can be used to turn on/off the condensed index at 506 | # top of each HTML page. The value NO (the default) enables the index and 507 | # the value YES disables it. 508 | 509 | DISABLE_INDEX = NO 510 | 511 | # This tag can be used to set the number of enum values (range [1..20]) 512 | # that doxygen will group on one line in the generated HTML documentation. 513 | 514 | ENUM_VALUES_PER_LINE = 4 515 | 516 | # If the GENERATE_TREEVIEW tag is set to YES, a side panel will be 517 | # generated containing a tree-like index structure (just like the one that 518 | # is generated for HTML Help). For this to work a browser that supports 519 | # JavaScript and frames is required (for instance Mozilla, Netscape 4.0+, 520 | # or Internet explorer 4.0+). Note that for large projects the tree generation 521 | # can take a very long time. In such cases it is better to disable this feature. 522 | # Windows users are probably better off using the HTML help feature. 523 | 524 | GENERATE_TREEVIEW = NO 525 | 526 | # If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be 527 | # used to set the initial width (in pixels) of the frame in which the tree 528 | # is shown. 529 | 530 | TREEVIEW_WIDTH = 250 531 | 532 | #--------------------------------------------------------------------------- 533 | # configuration options related to the LaTeX output 534 | #--------------------------------------------------------------------------- 535 | 536 | # If the GENERATE_LATEX tag is set to YES (the default) Doxygen will 537 | # generate Latex output. 538 | 539 | GENERATE_LATEX = YES 540 | 541 | # The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. 542 | # If a relative path is entered the value of OUTPUT_DIRECTORY will be 543 | # put in front of it. If left blank `latex' will be used as the default path. 544 | 545 | LATEX_OUTPUT = latex 546 | 547 | # The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be invoked. If left blank `latex' will be used as the default command name. 548 | 549 | LATEX_CMD_NAME = latex 550 | 551 | # The MAKEINDEX_CMD_NAME tag can be used to specify the command name to 552 | # generate index for LaTeX. If left blank `makeindex' will be used as the 553 | # default command name. 554 | 555 | MAKEINDEX_CMD_NAME = makeindex 556 | 557 | # If the COMPACT_LATEX tag is set to YES Doxygen generates more compact 558 | # LaTeX documents. This may be useful for small projects and may help to 559 | # save some trees in general. 560 | 561 | COMPACT_LATEX = NO 562 | 563 | # The PAPER_TYPE tag can be used to set the paper type that is used 564 | # by the printer. Possible values are: a4, a4wide, letter, legal and 565 | # executive. If left blank a4wide will be used. 566 | 567 | PAPER_TYPE = a4wide 568 | 569 | # The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX 570 | # packages that should be included in the LaTeX output. 571 | 572 | EXTRA_PACKAGES = 573 | 574 | # The LATEX_HEADER tag can be used to specify a personal LaTeX header for 575 | # the generated latex document. The header should contain everything until 576 | # the first chapter. If it is left blank doxygen will generate a 577 | # standard header. Notice: only use this tag if you know what you are doing! 578 | 579 | LATEX_HEADER = 580 | 581 | # If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated 582 | # is prepared for conversion to pdf (using ps2pdf). The pdf file will 583 | # contain links (just like the HTML output) instead of page references 584 | # This makes the output suitable for online browsing using a pdf viewer. 585 | 586 | PDF_HYPERLINKS = NO 587 | 588 | # If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of 589 | # plain latex in the generated Makefile. Set this option to YES to get a 590 | # higher quality PDF documentation. 591 | 592 | USE_PDFLATEX = NO 593 | 594 | # If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. 595 | # command to the generated LaTeX files. This will instruct LaTeX to keep 596 | # running if errors occur, instead of asking the user for help. 597 | # This option is also used when generating formulas in HTML. 598 | 599 | LATEX_BATCHMODE = NO 600 | 601 | #--------------------------------------------------------------------------- 602 | # configuration options related to the RTF output 603 | #--------------------------------------------------------------------------- 604 | 605 | # If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output 606 | # The RTF output is optimised for Word 97 and may not look very pretty with 607 | # other RTF readers or editors. 608 | 609 | GENERATE_RTF = NO 610 | 611 | # The RTF_OUTPUT tag is used to specify where the RTF docs will be put. 612 | # If a relative path is entered the value of OUTPUT_DIRECTORY will be 613 | # put in front of it. If left blank `rtf' will be used as the default path. 614 | 615 | RTF_OUTPUT = rtf 616 | 617 | # If the COMPACT_RTF tag is set to YES Doxygen generates more compact 618 | # RTF documents. This may be useful for small projects and may help to 619 | # save some trees in general. 620 | 621 | COMPACT_RTF = NO 622 | 623 | # If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated 624 | # will contain hyperlink fields. The RTF file will 625 | # contain links (just like the HTML output) instead of page references. 626 | # This makes the output suitable for online browsing using WORD or other 627 | # programs which support those fields. 628 | # Note: wordpad (write) and others do not support links. 629 | 630 | RTF_HYPERLINKS = NO 631 | 632 | # Load stylesheet definitions from file. Syntax is similar to doxygen's 633 | # config file, i.e. a series of assigments. You only have to provide 634 | # replacements, missing definitions are set to their default value. 635 | 636 | RTF_STYLESHEET_FILE = 637 | 638 | # Set optional variables used in the generation of an rtf document. 639 | # Syntax is similar to doxygen's config file. 640 | 641 | RTF_EXTENSIONS_FILE = 642 | 643 | #--------------------------------------------------------------------------- 644 | # configuration options related to the man page output 645 | #--------------------------------------------------------------------------- 646 | 647 | # If the GENERATE_MAN tag is set to YES (the default) Doxygen will 648 | # generate man pages 649 | 650 | GENERATE_MAN = NO 651 | 652 | # The MAN_OUTPUT tag is used to specify where the man pages will be put. 653 | # If a relative path is entered the value of OUTPUT_DIRECTORY will be 654 | # put in front of it. If left blank `man' will be used as the default path. 655 | 656 | MAN_OUTPUT = man 657 | 658 | # The MAN_EXTENSION tag determines the extension that is added to 659 | # the generated man pages (default is the subroutine's section .3) 660 | 661 | MAN_EXTENSION = .3 662 | 663 | # If the MAN_LINKS tag is set to YES and Doxygen generates man output, 664 | # then it will generate one additional man file for each entity 665 | # documented in the real man page(s). These additional files 666 | # only source the real man page, but without them the man command 667 | # would be unable to find the correct page. The default is NO. 668 | 669 | MAN_LINKS = NO 670 | 671 | #--------------------------------------------------------------------------- 672 | # configuration options related to the XML output 673 | #--------------------------------------------------------------------------- 674 | 675 | # If the GENERATE_XML tag is set to YES Doxygen will 676 | # generate an XML file that captures the structure of 677 | # the code including all documentation. Note that this 678 | # feature is still experimental and incomplete at the 679 | # moment. 680 | 681 | GENERATE_XML = NO 682 | 683 | #--------------------------------------------------------------------------- 684 | # configuration options for the AutoGen Definitions output 685 | #--------------------------------------------------------------------------- 686 | 687 | # If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will 688 | # generate an AutoGen Definitions (see autogen.sf.net) file 689 | # that captures the structure of the code including all 690 | # documentation. Note that this feature is still experimental 691 | # and incomplete at the moment. 692 | 693 | GENERATE_AUTOGEN_DEF = NO 694 | 695 | #--------------------------------------------------------------------------- 696 | # Configuration options related to the preprocessor 697 | #--------------------------------------------------------------------------- 698 | 699 | # If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will 700 | # evaluate all C-preprocessor directives found in the sources and include 701 | # files. 702 | 703 | ENABLE_PREPROCESSING = YES 704 | 705 | # If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro 706 | # names in the source code. If set to NO (the default) only conditional 707 | # compilation will be performed. Macro expansion can be done in a controlled 708 | # way by setting EXPAND_ONLY_PREDEF to YES. 709 | 710 | MACRO_EXPANSION = NO 711 | 712 | # If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES 713 | # then the macro expansion is limited to the macros specified with the 714 | # PREDEFINED and EXPAND_AS_PREDEFINED tags. 715 | 716 | EXPAND_ONLY_PREDEF = NO 717 | 718 | # If the SEARCH_INCLUDES tag is set to YES (the default) the includes files 719 | # in the INCLUDE_PATH (see below) will be search if a #include is found. 720 | 721 | SEARCH_INCLUDES = YES 722 | 723 | # The INCLUDE_PATH tag can be used to specify one or more directories that 724 | # contain include files that are not input files but should be processed by 725 | # the preprocessor. 726 | 727 | INCLUDE_PATH = 728 | 729 | # You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard 730 | # patterns (like *.h and *.hpp) to filter out the header-files in the 731 | # directories. If left blank, the patterns specified with FILE_PATTERNS will 732 | # be used. 733 | 734 | INCLUDE_FILE_PATTERNS = 735 | 736 | # The PREDEFINED tag can be used to specify one or more macro names that 737 | # are defined before the preprocessor is started (similar to the -D option of 738 | # gcc). The argument of the tag is a list of macros of the form: name 739 | # or name=definition (no spaces). If the definition and the = are 740 | # omitted =1 is assumed. 741 | 742 | PREDEFINED = 743 | 744 | # If the MACRO_EXPANSION and EXPAND_PREDEF_ONLY tags are set to YES then 745 | # this tag can be used to specify a list of macro names that should be expanded. 746 | # The macro definition that is found in the sources will be used. 747 | # Use the PREDEFINED tag if you want to use a different macro definition. 748 | 749 | EXPAND_AS_DEFINED = 750 | 751 | # If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then 752 | # doxygen's preprocessor will remove all function-like macros that are alone 753 | # on a line and do not end with a semicolon. Such function macros are typically 754 | # used for boiler-plate code, and will confuse the parser if not removed. 755 | 756 | SKIP_FUNCTION_MACROS = YES 757 | 758 | #--------------------------------------------------------------------------- 759 | # Configuration::addtions related to external references 760 | #--------------------------------------------------------------------------- 761 | 762 | # The TAGFILES tag can be used to specify one or more tagfiles. 763 | 764 | TAGFILES = 765 | 766 | # When a file name is specified after GENERATE_TAGFILE, doxygen will create 767 | # a tag file that is based on the input files it reads. 768 | 769 | GENERATE_TAGFILE = 770 | 771 | # If the ALLEXTERNALS tag is set to YES all external classes will be listed 772 | # in the class index. If set to NO only the inherited external classes 773 | # will be listed. 774 | 775 | ALLEXTERNALS = NO 776 | 777 | # If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed 778 | # in the modules index. If set to NO, only the current project's groups will 779 | # be listed. 780 | 781 | EXTERNAL_GROUPS = YES 782 | 783 | # The PERL_PATH should be the absolute path and name of the perl script 784 | # interpreter (i.e. the result of `which perl'). 785 | 786 | PERL_PATH = /usr/bin/perl 787 | 788 | #--------------------------------------------------------------------------- 789 | # Configuration options related to the dot tool 790 | #--------------------------------------------------------------------------- 791 | 792 | # If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will 793 | # generate a inheritance diagram (in Html, RTF and LaTeX) for classes with base or 794 | # super classes. Setting the tag to NO turns the diagrams off. Note that this 795 | # option is superceded by the HAVE_DOT option below. This is only a fallback. It is 796 | # recommended to install and use dot, since it yield more powerful graphs. 797 | 798 | CLASS_DIAGRAMS = YES 799 | 800 | # If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is 801 | # available from the path. This tool is part of Graphviz, a graph visualization 802 | # toolkit from AT&T and Lucent Bell Labs. The other options in this section 803 | # have no effect if this option is set to NO (the default) 804 | 805 | HAVE_DOT = NO 806 | 807 | # If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen 808 | # will generate a graph for each documented class showing the direct and 809 | # indirect inheritance relations. Setting this tag to YES will force the 810 | # the CLASS_DIAGRAMS tag to NO. 811 | 812 | CLASS_GRAPH = YES 813 | 814 | # If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen 815 | # will generate a graph for each documented class showing the direct and 816 | # indirect implementation dependencies (inheritance, containment, and 817 | # class references variables) of the class with other documented classes. 818 | 819 | COLLABORATION_GRAPH = YES 820 | 821 | # If set to YES, the inheritance and collaboration graphs will show the 822 | # relations between templates and their instances. 823 | 824 | TEMPLATE_RELATIONS = YES 825 | 826 | # If set to YES, the inheritance and collaboration graphs will hide 827 | # inheritance and usage relations if the target is undocumented 828 | # or is not a class. 829 | 830 | HIDE_UNDOC_RELATIONS = YES 831 | 832 | # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT 833 | # tags are set to YES then doxygen will generate a graph for each documented 834 | # file showing the direct and indirect include dependencies of the file with 835 | # other documented files. 836 | 837 | INCLUDE_GRAPH = YES 838 | 839 | # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and 840 | # HAVE_DOT tags are set to YES then doxygen will generate a graph for each 841 | # documented header file showing the documented files that directly or 842 | # indirectly include this file. 843 | 844 | INCLUDED_BY_GRAPH = YES 845 | 846 | # If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen 847 | # will graphical hierarchy of all classes instead of a textual one. 848 | 849 | GRAPHICAL_HIERARCHY = YES 850 | 851 | # The DOT_IMAGE_FORMAT tag can be used to set the image format of the images 852 | # generated by dot. Possible values are png, jpg, or gif 853 | # If left blank png will be used. 854 | 855 | DOT_IMAGE_FORMAT = png 856 | 857 | # The tag DOT_PATH can be used to specify the path where the dot tool can be 858 | # found. If left blank, it is assumed the dot tool can be found on the path. 859 | 860 | DOT_PATH = 861 | 862 | # The DOTFILE_DIRS tag can be used to specify one or more directories that 863 | # contain dot files that are included in the documentation (see the 864 | # \dotfile command). 865 | 866 | DOTFILE_DIRS = 867 | 868 | # The MAX_DOT_GRAPH_WIDTH tag can be used to set the maximum allowed width 869 | # (in pixels) of the graphs generated by dot. If a graph becomes larger than 870 | # this value, doxygen will try to truncate the graph, so that it fits within 871 | # the specified constraint. Beware that most browsers cannot cope with very 872 | # large images. 873 | 874 | MAX_DOT_GRAPH_WIDTH = 1024 875 | 876 | # The MAX_DOT_GRAPH_HEIGHT tag can be used to set the maximum allows height 877 | # (in pixels) of the graphs generated by dot. If a graph becomes larger than 878 | # this value, doxygen will try to truncate the graph, so that it fits within 879 | # the specified constraint. Beware that most browsers cannot cope with very 880 | # large images. 881 | 882 | MAX_DOT_GRAPH_HEIGHT = 1024 883 | 884 | # If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will 885 | # generate a legend page explaining the meaning of the various boxes and 886 | # arrows in the dot generated graphs. 887 | 888 | GENERATE_LEGEND = YES 889 | 890 | # If the DOT_CLEANUP tag is set to YES (the default) Doxygen will 891 | # remove the intermedate dot files that are used to generate 892 | # the various graphs. 893 | 894 | DOT_CLEANUP = YES 895 | 896 | #--------------------------------------------------------------------------- 897 | # Configuration::addtions related to the search engine 898 | #--------------------------------------------------------------------------- 899 | 900 | # The SEARCHENGINE tag specifies whether or not a search engine should be 901 | # used. If set to NO the values of all tags below this one will be ignored. 902 | 903 | SEARCHENGINE = NO 904 | 905 | # The CGI_NAME tag should be the name of the CGI script that 906 | # starts the search engine (doxysearch) with the correct parameters. 907 | # A script with this name will be generated by doxygen. 908 | 909 | CGI_NAME = search.cgi 910 | 911 | # The CGI_URL tag should be the absolute URL to the directory where the 912 | # cgi binaries are located. See the documentation of your http daemon for 913 | # details. 914 | 915 | CGI_URL = 916 | 917 | # The DOC_URL tag should be the absolute URL to the directory where the 918 | # documentation is located. If left blank the absolute path to the 919 | # documentation, with file:// prepended to it, will be used. 920 | 921 | DOC_URL = 922 | 923 | # The DOC_ABSPATH tag should be the absolute path to the directory where the 924 | # documentation is located. If left blank the directory on the local machine 925 | # will be used. 926 | 927 | DOC_ABSPATH = 928 | 929 | # The BIN_ABSPATH tag must point to the directory where the doxysearch binary 930 | # is installed. 931 | 932 | BIN_ABSPATH = /usr/local/bin/ 933 | 934 | # The EXT_DOC_PATHS tag can be used to specify one or more paths to 935 | # documentation generated for other projects. This allows doxysearch to search 936 | # the documentation for these projects as well. 937 | 938 | EXT_DOC_PATHS = 939 | -------------------------------------------------------------------------------- /include/oscpp/client.hpp: -------------------------------------------------------------------------------- 1 | // oscpp library 2 | // 3 | // Copyright (c) 2004-2013 Stefan Kersten 4 | // 5 | // Permission is hereby granted, free of charge, to any person or organization 6 | // obtaining a copy of the software and accompanying documentation covered by 7 | // this license (the "Software") to use, reproduce, display, distribute, 8 | // execute, and transmit the Software, and to prepare derivative works of the 9 | // Software, and to permit third-parties to whom the Software is furnished to 10 | // do so, all subject to the following: 11 | // 12 | // The copyright notices in the Software and this entire statement, including 13 | // the above license grant, this restriction and the following disclaimer, 14 | // must be included in all copies of the Software, in whole or in part, and 15 | // all derivative works of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT 20 | // SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE 21 | // FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, 22 | // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | // DEALINGS IN THE SOFTWARE. 24 | 25 | #ifndef OSCPP_CLIENT_HPP_INCLUDED 26 | #define OSCPP_CLIENT_HPP_INCLUDED 27 | 28 | #include 29 | #include 30 | #include 31 | 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | 38 | namespace OSCPP { namespace Client { 39 | 40 | //! OSC packet construction. 41 | /*! 42 | * Construct a valid OSC packet for transmitting over a transport 43 | * medium. 44 | */ 45 | class Packet 46 | { 47 | int32_t ptrDiff(const char* a, const char* b) 48 | { 49 | // Make sure pointer difference fits into int32_t 50 | const intptr_t diff = a - b; 51 | if (diff < std::numeric_limits::min() || 52 | diff > std::numeric_limits::max()) 53 | { 54 | std::stringstream s; 55 | s << "Pointer difference " << diff 56 | << " can't be represented by int32_t"; 57 | throw std::logic_error(s.str()); 58 | } 59 | return static_cast(diff); 60 | } 61 | 62 | int32_t calcSize(const char* begin, const char* end) 63 | { 64 | const int32_t size = ptrDiff(end, begin) - 4; 65 | if (size < 0) 66 | { 67 | throw std::logic_error("Calculated size is negative"); 68 | } 69 | return size; 70 | } 71 | 72 | public: 73 | //! Constructor. 74 | /*! 75 | */ 76 | Packet() 77 | { 78 | reset(0, 0); 79 | } 80 | 81 | //! Constructor. 82 | /*! 83 | */ 84 | Packet(void* buffer, size_t size) 85 | { 86 | reset(buffer, size); 87 | } 88 | 89 | //! Destructor. 90 | virtual ~Packet() 91 | {} 92 | 93 | //! Get packet buffer address. 94 | /*! 95 | * Return the start address of the packet currently under 96 | * construction. 97 | */ 98 | void* data() const 99 | { 100 | return m_buffer; 101 | } 102 | 103 | size_t capacity() const 104 | { 105 | return m_capacity; 106 | } 107 | 108 | //! Get packet content size. 109 | /*! 110 | * Return the size of the packet currently under construction. 111 | */ 112 | size_t size() const 113 | { 114 | return m_args.consumed(); 115 | } 116 | 117 | //! Reset packet state. 118 | void reset(void* buffer, size_t size) 119 | { 120 | checkAlignment(&m_buffer, kAlignment); 121 | m_buffer = buffer; 122 | m_capacity = size; 123 | m_args = WriteStream(m_buffer, m_capacity); 124 | m_sizePosM = m_sizePosB = nullptr; 125 | m_inBundle = 0; 126 | } 127 | 128 | void reset() 129 | { 130 | reset(m_buffer, m_capacity); 131 | } 132 | 133 | Packet& openBundle(uint64_t time) 134 | { 135 | if (m_inBundle > 0) 136 | { 137 | assert(m_sizePosB != nullptr || m_inBundle == 1); 138 | // Remember previous size pos offset 139 | const int32_t offset = 140 | m_sizePosB == nullptr ? 0 : ptrDiff(m_sizePosB, m_args.begin()); 141 | char* curPos = m_args.pos(); 142 | m_args.skip(4); 143 | // Record size pos 144 | std::memcpy(curPos, &offset, 4); 145 | m_sizePosB = curPos; 146 | } 147 | else if (m_args.pos() != m_args.begin()) 148 | { 149 | throw std::logic_error( 150 | "Cannot open toplevel bundle in non-empty packet"); 151 | } 152 | 153 | m_inBundle++; 154 | m_args.putString("#bundle"); 155 | m_args.putUInt64(time); 156 | return *this; 157 | } 158 | 159 | Packet& closeBundle() 160 | { 161 | if (m_inBundle > 0) 162 | { 163 | if (m_inBundle > 1) 164 | { 165 | // Get current stream pos 166 | char* curPos = m_args.pos(); 167 | 168 | // Get previous bundle size stream pos 169 | int32_t offset; 170 | memcpy(&offset, m_sizePosB, 4); 171 | // Get previous size pos 172 | char* prevPos = m_args.begin() + offset; 173 | 174 | const int32_t bundleSize = calcSize(m_sizePosB, curPos); 175 | assert(bundleSize >= 0 && 176 | (size_t)bundleSize >= Size::bundle(0)); 177 | // Write bundle size 178 | m_args.setPos(m_sizePosB); 179 | m_args.putInt32(bundleSize); 180 | m_args.setPos(curPos); 181 | 182 | // record outer bundle size pos 183 | m_sizePosB = prevPos; 184 | } 185 | m_inBundle--; 186 | } 187 | else 188 | { 189 | throw std::logic_error( 190 | "closeBundle() without matching openBundle()"); 191 | } 192 | return *this; 193 | } 194 | 195 | Packet& openMessage(const char* addr, size_t numTags) 196 | { 197 | if (m_inBundle > 0) 198 | { 199 | // record message size pos 200 | m_sizePosM = m_args.pos(); 201 | // advance arg stream 202 | m_args.skip(4); 203 | } 204 | m_args.putString(addr); 205 | size_t sigLen = numTags + 2; 206 | m_tags = WriteStream(m_args, sigLen); 207 | m_args.zero(align(sigLen)); 208 | m_tags.putChar(','); 209 | return *this; 210 | } 211 | 212 | Packet& closeMessage() 213 | { 214 | if (m_inBundle > 0) 215 | { 216 | // Get current stream pos 217 | char* curPos = m_args.pos(); 218 | // write message size 219 | m_args.setPos(m_sizePosM); 220 | m_args.putInt32(calcSize(m_sizePosM, curPos)); 221 | // restore stream pos 222 | m_args.setPos(curPos); 223 | // reset tag stream 224 | m_tags = WriteStream(); 225 | } 226 | return *this; 227 | } 228 | 229 | //! Write integer message argument. 230 | /*! 231 | * Write a 32 bit integer message argument. 232 | * 233 | * \param arg 32 bit integer argument. 234 | * 235 | * \pre openMessage must have been called before with no intervening 236 | * closeMessage. 237 | * 238 | * \throw OSCPP::XRunError stream buffer xrun. 239 | */ 240 | Packet& int32(int32_t arg) 241 | { 242 | m_tags.putChar('i'); 243 | m_args.putInt32(arg); 244 | return *this; 245 | } 246 | 247 | Packet& float32(float arg) 248 | { 249 | m_tags.putChar('f'); 250 | m_args.putFloat32(arg); 251 | return *this; 252 | } 253 | 254 | Packet& string(const char* arg) 255 | { 256 | m_tags.putChar('s'); 257 | m_args.putString(arg); 258 | return *this; 259 | } 260 | 261 | // @throw std::invalid_argument if blob size is greater than 262 | // std::numeric_limits::max() 263 | Packet& blob(const Blob& arg) 264 | { 265 | if (arg.size() > (size_t)std::numeric_limits::max()) 266 | { 267 | throw std::invalid_argument("Blob size greater than maximum " 268 | "value representable by int32_t"); 269 | } 270 | m_tags.putChar('b'); 271 | m_args.putInt32(static_cast(arg.size())); 272 | m_args.putData(arg.data(), arg.size()); 273 | return *this; 274 | } 275 | 276 | Packet& openArray() 277 | { 278 | m_tags.putChar('['); 279 | return *this; 280 | } 281 | 282 | Packet& closeArray() 283 | { 284 | m_tags.putChar(']'); 285 | return *this; 286 | } 287 | 288 | template Packet& put(T) 289 | { 290 | T::OSC_Client_Packet_put_unimplemented; 291 | return *this; 292 | } 293 | 294 | template 295 | Packet& put(InputIterator begin, InputIterator end) 296 | { 297 | for (auto it = begin; it != end; it++) 298 | { 299 | put(*it); 300 | } 301 | return *this; 302 | } 303 | 304 | template 305 | Packet& putArray(InputIterator begin, InputIterator end) 306 | { 307 | openArray(); 308 | put(begin, end); 309 | closeArray(); 310 | return *this; 311 | } 312 | 313 | private: 314 | void* m_buffer; 315 | size_t m_capacity; 316 | WriteStream m_args; // packet stream 317 | WriteStream m_tags; // current tag stream 318 | char* m_sizePosM; // last message size position 319 | char* m_sizePosB; // last bundle size position 320 | size_t m_inBundle; // bundle nesting depth 321 | }; 322 | 323 | template <> inline Packet& Packet::put(int32_t x) 324 | { 325 | return int32(x); 326 | } 327 | template <> inline Packet& Packet::put(float x) 328 | { 329 | return float32(x); 330 | } 331 | template <> inline Packet& Packet::put(const char* x) 332 | { 333 | return string(x); 334 | } 335 | template <> inline Packet& Packet::put(Blob x) 336 | { 337 | return blob(x); 338 | } 339 | 340 | template class StaticPacket : public Packet 341 | { 342 | public: 343 | StaticPacket() 344 | : Packet(reinterpret_cast(&m_buffer), buffer_size) 345 | {} 346 | 347 | private: 348 | typedef typename std::aligned_storage::type 349 | AlignedBuffer; 350 | AlignedBuffer m_buffer; 351 | }; 352 | 353 | class DynamicPacket : public Packet 354 | { 355 | public: 356 | DynamicPacket(size_t buffer_size) 357 | : Packet(static_cast(new char[buffer_size]), buffer_size) 358 | {} 359 | 360 | ~DynamicPacket() 361 | { 362 | delete[] static_cast(data()); 363 | } 364 | }; 365 | 366 | }} // namespace OSCPP::Client 367 | 368 | #endif // OSCPP_CLIENT_HPP_INCLUDED 369 | -------------------------------------------------------------------------------- /include/oscpp/detail/endian.hpp: -------------------------------------------------------------------------------- 1 | // Copyright 2005 Caleb Epstein 2 | // Copyright 2006 John Maddock 3 | // Copyright 2010 Rene Rivera 4 | // Distributed under the Boost Software License, Version 1.0. (See accompany- 5 | // ing file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | /* 8 | * Copyright (c) 1997 9 | * Silicon Graphics Computer Systems, Inc. 10 | * 11 | * Permission to use, copy, modify, distribute and sell this software 12 | * and its documentation for any purpose is hereby granted without fee, 13 | * provided that the above copyright notice appear in all copies and 14 | * that both that copyright notice and this permission notice appear 15 | * in supporting documentation. Silicon Graphics makes no 16 | * representations about the suitability of this software for any 17 | * purpose. It is provided "as is" without express or implied warranty. 18 | */ 19 | 20 | /* 21 | * Copyright notice reproduced from , from 22 | * which this code was originally taken. 23 | * 24 | * Modified by Caleb Epstein to use with GNU libc and to 25 | * defined the BOOST_ENDIAN macro. 26 | */ 27 | 28 | /* 29 | * Modifications for oscpp by Stefan Kersten 30 | * - Change prefix from BOOST to OSCPP 31 | * - Remove PDP endianness 32 | * - Add OSCPP_BYTE_ORDER_* macros 33 | */ 34 | 35 | #ifndef OSCPP_ENDIAN_HPP_INCLUDED 36 | #define OSCPP_ENDIAN_HPP_INCLUDED 37 | 38 | #define OSCPP_BYTE_ORDER_BIG_ENDIAN 4321 39 | #define OSCPP_BYTE_ORDER_LITTLE_ENDIAN 1234 40 | 41 | // GNU libc offers the helpful header which defines 42 | // __BYTE_ORDER 43 | 44 | #if defined(__GLIBC__) || defined(__ANDROID__) 45 | # include 46 | # if (__BYTE_ORDER == __LITTLE_ENDIAN) 47 | # define OSCPP_LITTLE_ENDIAN 48 | # elif (__BYTE_ORDER == __BIG_ENDIAN) 49 | # define OSCPP_BIG_ENDIAN 50 | # else 51 | # error Unknown machine endianness detected. 52 | # endif 53 | # define OSCPP_BYTE_ORDER __BYTE_ORDER 54 | #elif defined(_BIG_ENDIAN) && !defined(_LITTLE_ENDIAN) || \ 55 | defined(__BIG_ENDIAN__) && !defined(__LITTLE_ENDIAN__) || \ 56 | defined(_STLP_BIG_ENDIAN) && !defined(_STLP_LITTLE_ENDIAN) 57 | # define OSCPP_BIG_ENDIAN 58 | # define OSCPP_BYTE_ORDER OSCPP_BYTE_ORDER_BIG_ENDIAN 59 | #elif defined(_LITTLE_ENDIAN) && !defined(_BIG_ENDIAN) || \ 60 | defined(__LITTLE_ENDIAN__) && !defined(__BIG_ENDIAN__) || \ 61 | defined(_STLP_LITTLE_ENDIAN) && !defined(_STLP_BIG_ENDIAN) 62 | # define OSCPP_LITTLE_ENDIAN 63 | # define OSCPP_BYTE_ORDER OSCPP_BYTE_ORDER_LITTLE_ENDIAN 64 | #elif defined(__sparc) || defined(__sparc__) || defined(_POWER) || \ 65 | defined(__powerpc__) || defined(__ppc__) || defined(__hpux) || \ 66 | defined(__hppa) || defined(_MIPSEB) || defined(_POWER) || \ 67 | defined(__s390__) 68 | # define OSCPP_BIG_ENDIAN 69 | # define OSCPP_BYTE_ORDER OSCPP_BYTE_ORDER_BIG_ENDIAN 70 | #elif defined(__i386__) || defined(__alpha__) || defined(__ia64) || \ 71 | defined(__ia64__) || defined(_M_IX86) || defined(_M_IA64) || \ 72 | defined(_M_ALPHA) || defined(__amd64) || defined(__amd64__) || \ 73 | defined(_M_AMD64) || defined(__x86_64) || defined(__x86_64__) || \ 74 | defined(_M_X64) || defined(__bfin__) 75 | 76 | # define OSCPP_LITTLE_ENDIAN 77 | # define OSCPP_BYTE_ORDER OSCPP_BYTE_ORDER_LITTLE_ENDIAN 78 | #else 79 | # error The file oscpp/endian.hpp needs to be set up for your CPU type. 80 | #endif 81 | 82 | #endif // OSCPP_ENDIAN_HPP_INCLUDED 83 | -------------------------------------------------------------------------------- /include/oscpp/detail/host.hpp: -------------------------------------------------------------------------------- 1 | // oscpp library 2 | // 3 | // Copyright (c) 2004-2013 Stefan Kersten 4 | // 5 | // Permission is hereby granted, free of charge, to any person or organization 6 | // obtaining a copy of the software and accompanying documentation covered by 7 | // this license (the "Software") to use, reproduce, display, distribute, 8 | // execute, and transmit the Software, and to prepare derivative works of the 9 | // Software, and to permit third-parties to whom the Software is furnished to 10 | // do so, all subject to the following: 11 | // 12 | // The copyright notices in the Software and this entire statement, including 13 | // the above license grant, this restriction and the following disclaimer, 14 | // must be included in all copies of the Software, in whole or in part, and 15 | // all derivative works of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT 20 | // SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE 21 | // FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, 22 | // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | // DEALINGS IN THE SOFTWARE. 24 | 25 | #ifndef OSCPP_HOST_HPP_INCLUDED 26 | #define OSCPP_HOST_HPP_INCLUDED 27 | 28 | #include 29 | 30 | #include 31 | #include 32 | 33 | namespace OSCPP { 34 | #if defined(__GNUC__) 35 | inline static uint32_t bswap32(uint32_t x) 36 | { 37 | return __builtin_bswap32(x); 38 | } 39 | inline static uint64_t bswap64(uint64_t x) 40 | { 41 | return __builtin_bswap64(x); 42 | } 43 | #elif defined(_WINDOWS_) || defined(_WIN32) 44 | # include 45 | inline static uint32_t bswap32(uint32_t x) 46 | { 47 | return _byteswap_ulong(x); 48 | } 49 | inline static uint64_t bswap64(uint64_t x) 50 | { 51 | return _byteswap_uint64(x); 52 | } 53 | #else 54 | // Fallback implementation 55 | # warning Using unoptimized byte swap functions 56 | 57 | inline static uint32_t bswap32(uint32_t x) 58 | { 59 | const uint32_t b1 = x << 24; 60 | const uint32_t b2 = (x & 0x0000FF00) << 8; 61 | const uint32_t b3 = (x & 0x00FF0000) >> 8; 62 | const uint32_t b4 = x >> 24; 63 | return b1 | b2 | b3 | b4; 64 | } 65 | inline static uint64_t bswap64(int64_t x) 66 | { 67 | const uint64_t w1 = oscpp_bswap(uint32_t(x & 0x00000000FFFFFFFF)) << 32; 68 | const uint64_t w2 = oscpp_bswap(uint32_t(x >> 32)); 69 | return w1 | w2; 70 | } 71 | #endif 72 | 73 | enum ByteOrder 74 | { 75 | NetworkByteOrder, 76 | HostByteOrder 77 | }; 78 | 79 | template inline uint32_t convert32(uint32_t) 80 | { 81 | throw std::logic_error("Invalid byte order"); 82 | } 83 | 84 | template <> inline uint32_t convert32(uint32_t x) 85 | { 86 | #if defined(OSCPP_LITTLE_ENDIAN) 87 | return bswap32(x); 88 | #else 89 | return x; 90 | #endif 91 | } 92 | 93 | template <> inline uint32_t convert32(uint32_t x) 94 | { 95 | return x; 96 | } 97 | 98 | template inline uint64_t convert64(uint64_t) 99 | { 100 | throw std::logic_error("Invalid byte order"); 101 | } 102 | 103 | template <> inline uint64_t convert64(uint64_t x) 104 | { 105 | #if defined(OSCPP_LITTLE_ENDIAN) 106 | return bswap64(x); 107 | #else 108 | return x; 109 | #endif 110 | } 111 | 112 | template <> inline uint64_t convert64(uint64_t x) 113 | { 114 | return x; 115 | } 116 | } // namespace OSCPP 117 | 118 | #endif // OSCPP_HOST_HPP_INCLUDED 119 | -------------------------------------------------------------------------------- /include/oscpp/detail/stream.hpp: -------------------------------------------------------------------------------- 1 | // oscpp library 2 | // 3 | // Copyright (c) 2004-2013 Stefan Kersten 4 | // 5 | // Permission is hereby granted, free of charge, to any person or organization 6 | // obtaining a copy of the software and accompanying documentation covered by 7 | // this license (the "Software") to use, reproduce, display, distribute, 8 | // execute, and transmit the Software, and to prepare derivative works of the 9 | // Software, and to permit third-parties to whom the Software is furnished to 10 | // do so, all subject to the following: 11 | // 12 | // The copyright notices in the Software and this entire statement, including 13 | // the above license grant, this restriction and the following disclaimer, 14 | // must be included in all copies of the Software, in whole or in part, and 15 | // all derivative works of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT 20 | // SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE 21 | // FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, 22 | // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | // DEALINGS IN THE SOFTWARE. 24 | 25 | #ifndef OSCPP_STREAM_HPP_INCLUDED 26 | #define OSCPP_STREAM_HPP_INCLUDED 27 | 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | #include 34 | #include 35 | #include 36 | #include 37 | 38 | namespace OSCPP { 39 | 40 | class Stream 41 | { 42 | public: 43 | Stream() 44 | { 45 | m_begin = m_end = m_pos = 0; 46 | } 47 | 48 | Stream(void* data, size_t size) 49 | { 50 | m_begin = static_cast(data); 51 | m_end = m_begin + size; 52 | m_pos = m_begin; 53 | } 54 | 55 | Stream(const Stream& stream) 56 | { 57 | m_begin = m_pos = stream.m_pos; 58 | m_end = stream.m_end; 59 | } 60 | 61 | Stream(const Stream& stream, size_t size) 62 | { 63 | m_begin = m_pos = stream.m_pos; 64 | m_end = m_begin + size; 65 | if (m_end > stream.m_end) 66 | throw UnderrunError(); 67 | } 68 | 69 | void reset() 70 | { 71 | m_pos = m_begin; 72 | } 73 | 74 | const char* begin() const 75 | { 76 | return m_begin; 77 | } 78 | 79 | char* begin() 80 | { 81 | return m_begin; 82 | } 83 | 84 | const char* end() const 85 | { 86 | return m_end; 87 | } 88 | 89 | size_t capacity() const 90 | { 91 | return end() - begin(); 92 | } 93 | 94 | const char* pos() const 95 | { 96 | return m_pos; 97 | } 98 | 99 | char* pos() 100 | { 101 | return m_pos; 102 | } 103 | 104 | void setPos(char* pos) 105 | { 106 | assert((pos >= m_begin) && (pos <= m_end)); 107 | m_pos = pos; 108 | } 109 | 110 | void advance(size_t n) 111 | { 112 | m_pos += n; 113 | } 114 | 115 | bool atEnd() const 116 | { 117 | return pos() == end(); 118 | } 119 | 120 | size_t consumed() const 121 | { 122 | return pos() - begin(); 123 | } 124 | 125 | size_t consumable() const 126 | { 127 | return end() - pos(); 128 | } 129 | 130 | inline void checkAlignment(size_t n) const 131 | { 132 | OSCPP::checkAlignment(pos(), n); 133 | } 134 | 135 | protected: 136 | char* m_begin; 137 | char* m_end; 138 | char* m_pos; 139 | }; 140 | 141 | template class BasicWriteStream : public Stream 142 | { 143 | public: 144 | BasicWriteStream() 145 | : Stream() 146 | {} 147 | 148 | BasicWriteStream(void* data, size_t size) 149 | : Stream(data, size) 150 | {} 151 | 152 | BasicWriteStream(const BasicWriteStream& stream) 153 | : Stream(stream) 154 | {} 155 | 156 | BasicWriteStream(const BasicWriteStream& stream, size_t size) 157 | : Stream(stream, size) 158 | {} 159 | 160 | // throw (OverflowError) 161 | inline void checkWritable(size_t n) const 162 | { 163 | if (consumable() < n) 164 | throw OverflowError(n - consumable()); 165 | } 166 | 167 | void skip(size_t n) 168 | { 169 | checkWritable(n); 170 | advance(n); 171 | } 172 | 173 | void zero(size_t n) 174 | { 175 | checkWritable(n); 176 | std::memset(m_pos, 0, n); 177 | advance(n); 178 | } 179 | 180 | void putChar(char c) 181 | { 182 | checkWritable(1); 183 | *pos() = c; 184 | advance(1); 185 | } 186 | 187 | void putInt32(int32_t x) 188 | { 189 | checkWritable(4); 190 | checkAlignment(4); 191 | uint32_t uh; 192 | memcpy(&uh, &x, 4); 193 | const uint32_t un = convert32(uh); 194 | std::memcpy(pos(), &un, 4); 195 | advance(4); 196 | } 197 | 198 | void putUInt64(uint64_t x) 199 | { 200 | checkWritable(8); 201 | const uint64_t un = convert64(x); 202 | std::memcpy(pos(), &un, 8); 203 | advance(8); 204 | } 205 | 206 | void putFloat32(float f) 207 | { 208 | checkWritable(4); 209 | checkAlignment(4); 210 | uint32_t uh; 211 | std::memcpy(&uh, &f, 4); 212 | const uint32_t un = convert32(uh); 213 | std::memcpy(pos(), &un, 4); 214 | advance(4); 215 | } 216 | 217 | void putFloat64(double f) 218 | { 219 | checkWritable(8); 220 | checkAlignment(4); 221 | uint64_t uh; 222 | std::memcpy(&uh, &f, 8); 223 | const uint64_t un = convert64(uh); 224 | std::memcpy(pos(), &un, 8); 225 | advance(8); 226 | } 227 | 228 | void putData(const void* data, size_t size) 229 | { 230 | const size_t padding = OSCPP::padding(size); 231 | const size_t n = size + padding; 232 | checkWritable(n); 233 | std::memcpy(pos(), data, size); 234 | std::memset(pos() + size, 0, padding); 235 | advance(n); 236 | } 237 | 238 | void putString(const char* s) 239 | { 240 | putData(s, strlen(s) + 1); 241 | } 242 | }; 243 | 244 | typedef BasicWriteStream WriteStream; 245 | 246 | template class BasicReadStream : public Stream 247 | { 248 | public: 249 | BasicReadStream() 250 | {} 251 | 252 | BasicReadStream(const void* data, size_t size) 253 | : Stream(const_cast(data), size) 254 | {} 255 | 256 | BasicReadStream(const BasicReadStream& stream) 257 | : Stream(stream) 258 | {} 259 | 260 | BasicReadStream(const BasicReadStream& stream, size_t size) 261 | : Stream(stream, size) 262 | {} 263 | 264 | // throw (UnderrunError) 265 | void checkReadable(size_t n) const 266 | { 267 | if (consumable() < n) 268 | throw UnderrunError(); 269 | } 270 | 271 | // throw (UnderrunError) 272 | void skip(size_t n) 273 | { 274 | checkReadable(n); 275 | advance(n); 276 | } 277 | 278 | // throw (UnderrunError) 279 | inline char peekChar() const 280 | { 281 | checkReadable(1); 282 | return *pos(); 283 | } 284 | 285 | // throw (UnderrunError) 286 | inline char getChar() 287 | { 288 | const char x = peekChar(); 289 | advance(1); 290 | return x; 291 | } 292 | 293 | // throw (UnderrunError) 294 | inline int32_t peekInt32() const 295 | { 296 | checkReadable(4); 297 | checkAlignment(4); 298 | uint32_t un; 299 | std::memcpy(&un, pos(), 4); 300 | const uint32_t uh = convert32(un); 301 | int32_t x; 302 | std::memcpy(&x, &uh, 4); 303 | return x; 304 | } 305 | 306 | // throw (UnderrunError) 307 | inline int32_t getInt32() 308 | { 309 | const int32_t x = peekInt32(); 310 | advance(4); 311 | return x; 312 | } 313 | 314 | // throw (UnderrunError) 315 | inline uint64_t getUInt64() 316 | { 317 | checkReadable(8); 318 | uint64_t un; 319 | std::memcpy(&un, pos(), 8); 320 | advance(8); 321 | return convert64(un); 322 | } 323 | 324 | // throw (UnderrunError) 325 | inline float getFloat32() 326 | { 327 | checkReadable(4); 328 | checkAlignment(4); 329 | uint32_t un; 330 | std::memcpy(&un, pos(), 4); 331 | advance(4); 332 | const uint32_t uh = convert32(un); 333 | float f; 334 | std::memcpy(&f, &uh, 4); 335 | return f; 336 | } 337 | 338 | // throw (UnderrunError) 339 | inline double getFloat64() 340 | { 341 | checkReadable(8); 342 | checkAlignment(4); 343 | uint64_t un; 344 | std::memcpy(&un, pos(), 8); 345 | advance(8); 346 | const uint64_t uh = convert64(un); 347 | double f; 348 | std::memcpy(&f, &uh, 8); 349 | return f; 350 | } 351 | 352 | // throw (UnderrunError, ParseError) 353 | const char* getString() 354 | { 355 | checkReadable(4); // min string length 356 | 357 | const char* ptr = static_cast(pos()) + 3; 358 | const char* end = static_cast(this->end()); 359 | 360 | while (true) 361 | { 362 | if (ptr >= end) 363 | throw UnderrunError(); 364 | if (*ptr == '\0') 365 | break; 366 | ptr += 4; 367 | } 368 | 369 | const char* x = pos(); 370 | advance(ptr - pos() + 1); 371 | 372 | return x; 373 | } 374 | }; 375 | 376 | typedef BasicReadStream ReadStream; 377 | } // namespace OSCPP 378 | 379 | #endif // OSCPP_STREAM_HPP_INCLUDED 380 | -------------------------------------------------------------------------------- /include/oscpp/error.hpp: -------------------------------------------------------------------------------- 1 | // oscpp library 2 | // 3 | // Copyright (c) 2004-2013 Stefan Kersten 4 | // 5 | // Permission is hereby granted, free of charge, to any person or organization 6 | // obtaining a copy of the software and accompanying documentation covered by 7 | // this license (the "Software") to use, reproduce, display, distribute, 8 | // execute, and transmit the Software, and to prepare derivative works of the 9 | // Software, and to permit third-parties to whom the Software is furnished to 10 | // do so, all subject to the following: 11 | // 12 | // The copyright notices in the Software and this entire statement, including 13 | // the above license grant, this restriction and the following disclaimer, 14 | // must be included in all copies of the Software, in whole or in part, and 15 | // all derivative works of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT 20 | // SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE 21 | // FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, 22 | // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | // DEALINGS IN THE SOFTWARE. 24 | 25 | #ifndef OSCPP_ERROR_HPP_INCLUDED 26 | #define OSCPP_ERROR_HPP_INCLUDED 27 | 28 | #include 29 | #include 30 | 31 | namespace OSCPP { 32 | 33 | class Error : public std::exception 34 | { 35 | public: 36 | Error(const std::string& what) 37 | : m_what(what) 38 | {} 39 | 40 | virtual ~Error() noexcept 41 | {} 42 | 43 | const char* what() const noexcept override 44 | { 45 | return m_what.c_str(); 46 | } 47 | 48 | private: 49 | std::string m_what; 50 | }; 51 | 52 | class UnderrunError : public Error 53 | { 54 | public: 55 | UnderrunError() 56 | : Error(std::string("Buffer underrun")) 57 | {} 58 | }; 59 | 60 | class OverflowError : public Error 61 | { 62 | public: 63 | OverflowError(size_t bytes) 64 | : Error(std::string("Buffer overflow")) 65 | , m_bytes(bytes) 66 | {} 67 | 68 | size_t numBytes() const 69 | { 70 | return m_bytes; 71 | } 72 | 73 | private: 74 | size_t m_bytes; 75 | }; 76 | 77 | class ParseError : public Error 78 | { 79 | public: 80 | ParseError(const std::string& what = "Parse error") 81 | : Error(what) 82 | {} 83 | }; 84 | 85 | } // namespace OSCPP 86 | 87 | #endif // OSCPP_ERROR_HPP_INCLUDED 88 | -------------------------------------------------------------------------------- /include/oscpp/print.hpp: -------------------------------------------------------------------------------- 1 | // OSCpp library 2 | // 3 | // Copyright (c) 2004-2011 Stefan Kersten 4 | // 5 | // Permission is hereby granted, free of charge, to any person or organization 6 | // obtaining a copy of the software and accompanying documentation covered by 7 | // this license (the "Software") to use, reproduce, display, distribute, 8 | // execute, and transmit the Software, and to prepare derivative works of the 9 | // Software, and to permit third-parties to whom the Software is furnished to 10 | // do so, all subject to the following: 11 | // 12 | // The copyright notices in the Software and this entire statement, including 13 | // the above license grant, this restriction and the following disclaimer, 14 | // must be included in all copies of the Software, in whole or in part, and 15 | // all derivative works of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT 20 | // SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE 21 | // FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, 22 | // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | // DEALINGS IN THE SOFTWARE. 24 | 25 | #ifndef OSCPP_PRINT_HPP_INCLUDED 26 | #define OSCPP_PRINT_HPP_INCLUDED 27 | 28 | #include 29 | #include 30 | 31 | #include 32 | 33 | namespace OSCPP { namespace detail { 34 | 35 | const size_t kDefaultIndentWidth = 4; 36 | 37 | class Indent 38 | { 39 | public: 40 | Indent(size_t w) 41 | : m_width(w) 42 | , m_indent(0) 43 | {} 44 | Indent(size_t w, size_t n) 45 | : m_width(w) 46 | , m_indent(n) 47 | {} 48 | Indent(const Indent&) = default; 49 | 50 | operator size_t() const 51 | { 52 | return m_indent; 53 | } 54 | Indent inc() const 55 | { 56 | return Indent(m_width, m_indent + m_width); 57 | } 58 | 59 | private: 60 | size_t m_width; 61 | size_t m_indent; 62 | }; 63 | 64 | inline std::ostream& operator<<(std::ostream& out, const Indent& indent) 65 | { 66 | size_t n = indent; 67 | while (n-- > 0) 68 | out << ' '; 69 | return out; 70 | } 71 | 72 | inline void printArgs(std::ostream& out, Server::ArgStream args) 73 | { 74 | while (!args.atEnd()) 75 | { 76 | const char t = args.tag(); 77 | switch (t) 78 | { 79 | case 'i': 80 | out << "i:" << args.int32(); 81 | break; 82 | case 'f': 83 | out << "f:" << args.float32(); 84 | break; 85 | case 's': 86 | out << "s:" << args.string(); 87 | break; 88 | case 'b': 89 | out << "b:" << args.blob().size(); 90 | break; 91 | case '[': 92 | out << "[ "; 93 | printArgs(out, args.array()); 94 | out << " ]"; 95 | break; 96 | default: 97 | out << t << ":?"; 98 | args.drop(); 99 | break; 100 | } 101 | out << ' '; 102 | } 103 | } 104 | 105 | inline void printMessage(std::ostream& out, const Server::Message& msg, 106 | const Indent& indent) 107 | { 108 | out << indent << msg.address() << ' '; 109 | printArgs(out, msg.args()); 110 | } 111 | 112 | inline void printBundle(std::ostream& out, const Server::Bundle& bundle, 113 | const Indent& indent) 114 | { 115 | out << indent << "# " << bundle.time() << " [" << std::endl; 116 | Indent nextIndent = indent.inc(); 117 | auto packets = bundle.packets(); 118 | while (!packets.atEnd()) 119 | { 120 | auto packet = packets.next(); 121 | if (packet.isMessage()) 122 | { 123 | printMessage(out, packet, nextIndent); 124 | } 125 | else 126 | { 127 | printBundle(out, packet, nextIndent); 128 | } 129 | out << std::endl; 130 | } 131 | out << indent << "]"; 132 | } 133 | 134 | inline void printPacket(std::ostream& out, const Server::Packet& packet, 135 | const Indent& indent) 136 | { 137 | if (packet.isMessage()) 138 | { 139 | printMessage(out, packet, indent); 140 | } 141 | else 142 | { 143 | printBundle(out, packet, indent); 144 | } 145 | } 146 | 147 | }} // namespace OSCPP::detail 148 | 149 | namespace OSCPP { namespace Server { 150 | 151 | inline std::ostream& operator<<(std::ostream& out, const Packet& packet) 152 | { 153 | detail::printPacket(out, packet, 154 | detail::Indent(detail::kDefaultIndentWidth)); 155 | return out; 156 | } 157 | 158 | inline std::ostream& operator<<(std::ostream& out, const Bundle& packet) 159 | { 160 | detail::printBundle(out, packet, 161 | detail::Indent(detail::kDefaultIndentWidth)); 162 | return out; 163 | } 164 | 165 | inline std::ostream& operator<<(std::ostream& out, const Message& packet) 166 | { 167 | detail::printMessage(out, packet, 168 | detail::Indent(detail::kDefaultIndentWidth)); 169 | return out; 170 | } 171 | 172 | }} // namespace OSCPP::Server 173 | 174 | namespace OSCPP { namespace Client { 175 | 176 | inline std::ostream& operator<<(std::ostream& out, const Packet& packet) 177 | { 178 | return out << Server::Packet(packet.data(), packet.size()); 179 | } 180 | 181 | }} // namespace OSCPP::Client 182 | 183 | #endif // OSCPP_PRINT_HPP_INCLUDED 184 | -------------------------------------------------------------------------------- /include/oscpp/server.hpp: -------------------------------------------------------------------------------- 1 | // oscpp library 2 | // 3 | // Copyright (c) 2004-2013 Stefan Kersten 4 | // 5 | // Permission is hereby granted, free of charge, to any person or organization 6 | // obtaining a copy of the software and accompanying documentation covered by 7 | // this license (the "Software") to use, reproduce, display, distribute, 8 | // execute, and transmit the Software, and to prepare derivative works of the 9 | // Software, and to permit third-parties to whom the Software is furnished to 10 | // do so, all subject to the following: 11 | // 12 | // The copyright notices in the Software and this entire statement, including 13 | // the above license grant, this restriction and the following disclaimer, 14 | // must be included in all copies of the Software, in whole or in part, and 15 | // all derivative works of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT 20 | // SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE 21 | // FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, 22 | // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | // DEALINGS IN THE SOFTWARE. 24 | 25 | #ifndef OSCPP_SERVER_HPP_INCLUDED 26 | #define OSCPP_SERVER_HPP_INCLUDED 27 | 28 | #include 29 | #include 30 | 31 | #include 32 | #include 33 | #include 34 | #include 35 | 36 | namespace OSCPP { namespace Server { 37 | 38 | //! OSC Message Argument Iterator. 39 | /*! 40 | * Retrieve typed arguments from an incoming message. 41 | * 42 | * Supported tags and their correspondong types are: 43 | * 44 | * i -- 32 bit signed integer number
45 | * f -- 32 bit floating point number
46 | * s -- NULL-terminated string padded to 4-byte boundary
47 | * b -- 32-bit integer size followed by 4-byte aligned data 48 | * 49 | * \sa getArgInt32 50 | * \sa getArgFloat32 51 | * \sa getArgString 52 | */ 53 | class ArgStream 54 | { 55 | public: 56 | //* Empty argument stream. 57 | ArgStream() = default; 58 | 59 | //* Construct argument stream from tag and value streams. 60 | ArgStream(const ReadStream& tags, const ReadStream& args) 61 | : m_tags(tags) 62 | , m_args(args) 63 | {} 64 | 65 | //! Constructor. 66 | /*! 67 | * Read arguments from stream, which has to point to the start of a 68 | * message type signature. 69 | * 70 | * \throw OSCPP::UnderrunError stream buffer underrun. 71 | * \throw OSCPP::ParseError error while parsing input stream. 72 | */ 73 | ArgStream(const ReadStream& stream) 74 | { 75 | m_args = stream; 76 | const char* tags = m_args.getString(); 77 | if (tags[0] != ',') 78 | throw ParseError("Tag string doesn't start with ','"); 79 | m_tags = ReadStream(tags + 1, strlen(tags) - 1); 80 | } 81 | 82 | //* Return the number of arguments that can be read from the stream. 83 | size_t size() const 84 | { 85 | return m_tags.capacity(); 86 | } 87 | 88 | //* Return true if no more arguments can be read from the stream. 89 | bool atEnd() const 90 | { 91 | return m_tags.atEnd(); 92 | } 93 | 94 | //* Return tag and argument streams. 95 | std::tuple state() const 96 | { 97 | return std::make_tuple(m_tags, m_args); 98 | } 99 | 100 | //* Return the type tag corresponding to the next message argument. 101 | char tag() const 102 | { 103 | return m_tags.peekChar(); 104 | } 105 | 106 | //* Drop next argument. 107 | void drop() 108 | { 109 | drop(m_tags.getChar()); 110 | } 111 | 112 | //! Get next integer argument. 113 | /*! 114 | * Read next numerical argument from the input stream and convert it 115 | * to an integer. 116 | * 117 | * \exception OSCPP::UnderrunError stream buffer underrun. 118 | * \exception OSCPP::ParseError argument could not be converted. 119 | */ 120 | int32_t int32() 121 | { 122 | const char t = m_tags.getChar(); 123 | if (t == 'i') 124 | return m_args.getInt32(); 125 | if (t == 'f') 126 | return (int32_t)m_args.getFloat32(); 127 | throw ParseError("Cannot convert argument to int"); 128 | } 129 | 130 | //! Get next float argument. 131 | /*! 132 | * Read next numerical argument from the input stream and convert it 133 | * to a float. 134 | * 135 | * \exception OSCPP::UnderrunError stream buffer underrun. 136 | * \exception OSCPP::ParseError argument could not be converted. 137 | */ 138 | float float32() 139 | { 140 | const char t = m_tags.getChar(); 141 | if (t == 'f') 142 | return m_args.getFloat32(); 143 | if (t == 'i') 144 | return (float)m_args.getInt32(); 145 | throw ParseError("Cannot convert argument to float"); 146 | } 147 | 148 | //! Get next string argument. 149 | /*! 150 | * Read next string argument and return it as a NULL-terminated 151 | * string. 152 | * 153 | * \exception OSCPP::UnderrunError stream buffer underrun. 154 | * \exception OSCPP::ParseError argument could not be converted or 155 | * is not a valid string. 156 | */ 157 | const char* string() 158 | { 159 | if (m_tags.getChar() == 's') 160 | { 161 | return m_args.getString(); 162 | } 163 | throw ParseError("Cannot convert argument to string"); 164 | } 165 | 166 | //* Get next blob argument. 167 | // 168 | // @throw OSCPP::UnderrunError stream buffer underrun. 169 | // @throw OSCPP::ParseError argument is not a valid blob 170 | Blob blob() 171 | { 172 | if (m_tags.getChar() == 'b') 173 | { 174 | return parseBlob(); 175 | } 176 | else 177 | { 178 | throw ParseError("Cannot convert argument to blob"); 179 | } 180 | } 181 | 182 | //* Return a stream corresponding to an array argument. 183 | ArgStream array() 184 | { 185 | if (m_tags.getChar() == '[') 186 | { 187 | const char* tags = m_tags.pos(); 188 | const char* args = m_args.pos(); 189 | dropArray(); 190 | // m_tags.pos() points right after the closing ']'. 191 | return ArgStream(ReadStream(tags, m_tags.pos() - tags - 1), 192 | ReadStream(args, m_args.pos() - args)); 193 | } 194 | else 195 | { 196 | throw ParseError("Expected array"); 197 | } 198 | } 199 | 200 | template T next() 201 | { 202 | return T::OSC_Server_ArgStream_next_unimplemented; 203 | } 204 | 205 | private: 206 | // Parse a blob (type tag already consumed). 207 | Blob parseBlob() 208 | { 209 | int32_t size = m_args.getInt32(); 210 | if (size < 0) 211 | { 212 | throw ParseError("Invalid blob size is less than zero"); 213 | } 214 | else 215 | { 216 | static_assert( 217 | sizeof(size_t) >= sizeof(int32_t), 218 | "Size of size_t must be greater than size of int32_t"); 219 | const void* data = m_args.pos(); 220 | m_args.skip(align(size)); 221 | return Blob(data, static_cast(size)); 222 | } 223 | } 224 | // Drop an atomic value of type t (type tag already consumed). 225 | void dropAtom(char t) 226 | { 227 | switch (t) 228 | { 229 | case 'i': 230 | m_args.skip(4); 231 | break; 232 | case 'f': 233 | m_args.skip(4); 234 | break; 235 | case 's': 236 | m_args.getString(); 237 | break; 238 | case 'b': 239 | parseBlob(); 240 | break; 241 | } 242 | } 243 | // Drop a possibly nested array. 244 | void dropArray() 245 | { 246 | unsigned int level = 0; 247 | for (;;) 248 | { 249 | char t = m_tags.getChar(); 250 | if (t == ']') 251 | { 252 | if (level == 0) 253 | break; 254 | else 255 | level--; 256 | } 257 | else if (t == '[') 258 | { 259 | level++; 260 | } 261 | else 262 | { 263 | dropAtom(t); 264 | } 265 | } 266 | } 267 | // Drop the next argument of type t (type tag already consumed). 268 | void drop(char t) 269 | { 270 | switch (t) 271 | { 272 | case '[': 273 | dropArray(); 274 | break; 275 | default: 276 | dropAtom(t); 277 | } 278 | } 279 | 280 | private: 281 | ReadStream m_tags; 282 | ReadStream m_args; 283 | }; 284 | 285 | class Message 286 | { 287 | public: 288 | Message(const char* address, const ReadStream& stream) 289 | : m_address(address) 290 | , m_args(ArgStream(stream)) 291 | {} 292 | 293 | const char* address() const 294 | { 295 | return m_address; 296 | } 297 | 298 | ArgStream args() const 299 | { 300 | return m_args; 301 | } 302 | 303 | private: 304 | const char* m_address; 305 | ArgStream m_args; 306 | }; 307 | 308 | class PacketStream; 309 | 310 | class Bundle 311 | { 312 | public: 313 | Bundle(uint64_t time, const ReadStream& stream) 314 | : m_time(time) 315 | , m_stream(stream) 316 | {} 317 | 318 | uint64_t time() const 319 | { 320 | return m_time; 321 | } 322 | 323 | inline PacketStream packets() const; 324 | 325 | private: 326 | uint64_t m_time; 327 | ReadStream m_stream; 328 | }; 329 | 330 | class Packet 331 | { 332 | public: 333 | Packet() 334 | : m_isBundle(false) 335 | {} 336 | 337 | Packet(const ReadStream& stream) 338 | : m_stream(stream) 339 | , m_isBundle(isBundle(stream)) 340 | { 341 | // Skip over #bundle header 342 | if (m_isBundle) 343 | m_stream.skip(8); 344 | } 345 | 346 | Packet(const void* data, size_t size) 347 | : Packet(ReadStream(data, size)) 348 | {} 349 | 350 | const void* data() const 351 | { 352 | return m_stream.begin(); 353 | } 354 | 355 | size_t size() const 356 | { 357 | return m_stream.capacity(); 358 | } 359 | 360 | bool isBundle() const 361 | { 362 | return m_isBundle; 363 | } 364 | 365 | bool isMessage() const 366 | { 367 | return !isBundle(); 368 | } 369 | 370 | operator Bundle() const 371 | { 372 | if (!isBundle()) 373 | throw ParseError("Packet is not a bundle"); 374 | ReadStream stream(m_stream); 375 | uint64_t time = stream.getUInt64(); 376 | return Bundle(time, std::move(stream)); 377 | } 378 | 379 | operator Message() const 380 | { 381 | if (!isMessage()) 382 | throw ParseError("Packet is not a message"); 383 | ReadStream stream(m_stream); 384 | const char* address = stream.getString(); 385 | return Message(address, std::move(stream)); 386 | } 387 | 388 | static bool isMessage(const void* data, size_t size) 389 | { 390 | return (size > 3) && (static_cast(data)[0] != '#'); 391 | } 392 | 393 | static bool isMessage(const ReadStream& stream) 394 | { 395 | return isMessage(stream.pos(), stream.consumable()); 396 | } 397 | 398 | static bool isBundle(const void* data, size_t size) 399 | { 400 | return (size > 15) && (std::memcmp(data, "#bundle", 8) == 0); 401 | } 402 | 403 | static bool isBundle(const ReadStream& stream) 404 | { 405 | return isBundle(stream.pos(), stream.consumable()); 406 | } 407 | 408 | private: 409 | ReadStream m_stream; 410 | bool m_isBundle; 411 | }; 412 | 413 | class PacketStream 414 | { 415 | public: 416 | PacketStream(const ReadStream& stream) 417 | : m_stream(stream) 418 | {} 419 | 420 | bool atEnd() const 421 | { 422 | return m_stream.atEnd(); 423 | } 424 | 425 | Packet next() 426 | { 427 | size_t size = m_stream.getInt32(); 428 | ReadStream stream(m_stream, size); 429 | m_stream.skip(size); 430 | return Packet(stream); 431 | } 432 | 433 | private: 434 | ReadStream m_stream; 435 | }; 436 | 437 | template <> inline int32_t ArgStream::next() 438 | { 439 | return int32(); 440 | } 441 | 442 | template <> inline float ArgStream::next() 443 | { 444 | return float32(); 445 | } 446 | 447 | template <> inline const char* ArgStream::next() 448 | { 449 | return string(); 450 | } 451 | 452 | template <> inline Blob ArgStream::next() 453 | { 454 | return blob(); 455 | } 456 | 457 | template <> inline ArgStream ArgStream::next() 458 | { 459 | return array(); 460 | } 461 | 462 | PacketStream Bundle::packets() const 463 | { 464 | return PacketStream(m_stream); 465 | } 466 | 467 | }} // namespace OSCPP::Server 468 | 469 | static inline bool operator==(const OSCPP::Server::Message& msg, 470 | const char* str) 471 | { 472 | return strcmp(msg.address(), str) == 0; 473 | } 474 | 475 | static inline bool operator==(const char* str, 476 | const OSCPP::Server::Message& msg) 477 | { 478 | return msg == str; 479 | } 480 | 481 | static inline bool operator!=(const OSCPP::Server::Message& msg, 482 | const char* str) 483 | { 484 | return !(msg == str); 485 | } 486 | 487 | static inline bool operator!=(const char* str, 488 | const OSCPP::Server::Message& msg) 489 | { 490 | return msg != str; 491 | } 492 | 493 | #endif // OSCPP_SERVER_HPP_INCLUDED 494 | -------------------------------------------------------------------------------- /include/oscpp/types.hpp: -------------------------------------------------------------------------------- 1 | // oscpp library 2 | // 3 | // Copyright (c) 2004-2013 Stefan Kersten 4 | // 5 | // Permission is hereby granted, free of charge, to any person or organization 6 | // obtaining a copy of the software and accompanying documentation covered by 7 | // this license (the "Software") to use, reproduce, display, distribute, 8 | // execute, and transmit the Software, and to prepare derivative works of the 9 | // Software, and to permit third-parties to whom the Software is furnished to 10 | // do so, all subject to the following: 11 | // 12 | // The copyright notices in the Software and this entire statement, including 13 | // the above license grant, this restriction and the following disclaimer, 14 | // must be included in all copies of the Software, in whole or in part, and 15 | // all derivative works of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT 20 | // SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE 21 | // FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, 22 | // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | // DEALINGS IN THE SOFTWARE. 24 | 25 | #ifndef OSCPP_TYPES_HPP_INCLUDED 26 | #define OSCPP_TYPES_HPP_INCLUDED 27 | 28 | namespace OSCPP { 29 | 30 | class Blob 31 | { 32 | public: 33 | Blob() 34 | : m_size(0) 35 | , m_data(nullptr) 36 | {} 37 | Blob(const void* data, size_t size) 38 | : m_size(size) 39 | , m_data(data) 40 | {} 41 | Blob(const Blob& other) = default; 42 | 43 | size_t size() const 44 | { 45 | return m_size; 46 | } 47 | const void* data() const 48 | { 49 | return m_data; 50 | } 51 | 52 | private: 53 | size_t m_size; 54 | const void* m_data; 55 | }; 56 | 57 | } // namespace OSCPP 58 | 59 | #endif // OSCPP_TYPES_HPP_INCLUDED 60 | -------------------------------------------------------------------------------- /include/oscpp/util.hpp: -------------------------------------------------------------------------------- 1 | // oscpp library 2 | // 3 | // Copyright (c) 2004-2013 Stefan Kersten 4 | // 5 | // Permission is hereby granted, free of charge, to any person or organization 6 | // obtaining a copy of the software and accompanying documentation covered by 7 | // this license (the "Software") to use, reproduce, display, distribute, 8 | // execute, and transmit the Software, and to prepare derivative works of the 9 | // Software, and to permit third-parties to whom the Software is furnished to 10 | // do so, all subject to the following: 11 | // 12 | // The copyright notices in the Software and this entire statement, including 13 | // the above license grant, this restriction and the following disclaimer, 14 | // must be included in all copies of the Software, in whole or in part, and 15 | // all derivative works of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT 20 | // SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE 21 | // FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, 22 | // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | // DEALINGS IN THE SOFTWARE. 24 | 25 | #ifndef OSCPP_UTIL_HPP_INCLUDED 26 | #define OSCPP_UTIL_HPP_INCLUDED 27 | 28 | #include 29 | #include 30 | 31 | namespace OSCPP { 32 | 33 | static const size_t kAlignment = 4; 34 | 35 | inline bool isAligned(const void* ptr, size_t alignment) 36 | { 37 | return (reinterpret_cast(ptr) & (alignment - 1)) == 0; 38 | } 39 | 40 | constexpr bool isAligned(size_t n) 41 | { 42 | return (n & 3) == 0; 43 | } 44 | 45 | constexpr size_t align(size_t n) 46 | { 47 | return (n + 3) & -4; 48 | } 49 | 50 | constexpr size_t padding(size_t n) 51 | { 52 | return align(n) - n; 53 | } 54 | 55 | inline void checkAlignment(const void* ptr, size_t n) 56 | { 57 | if (!isAligned(ptr, n)) 58 | { 59 | throw std::runtime_error("Unaligned pointer"); 60 | } 61 | } 62 | 63 | namespace Tags { 64 | 65 | constexpr size_t int32() 66 | { 67 | return 1; 68 | } 69 | constexpr size_t float32() 70 | { 71 | return 1; 72 | } 73 | constexpr size_t string() 74 | { 75 | return 1; 76 | } 77 | constexpr size_t blob() 78 | { 79 | return 1; 80 | } 81 | constexpr size_t array(size_t numElems) 82 | { 83 | return numElems + 2; 84 | } 85 | } // namespace Tags 86 | 87 | namespace Size { 88 | 89 | class String 90 | { 91 | public: 92 | String(const char* x) 93 | : m_value(x) 94 | {} 95 | 96 | operator const char*() const 97 | { 98 | return m_value; 99 | } 100 | 101 | private: 102 | const char* m_value; 103 | }; 104 | 105 | inline size_t string(const String& x) 106 | { 107 | return align(std::strlen(x) + 1); 108 | } 109 | 110 | template constexpr size_t string(char const (&)[N]) 111 | { 112 | return align(N); 113 | } 114 | 115 | constexpr size_t bundle(size_t numPackets) 116 | { 117 | return 8 /* #bundle */ + 8 /* timestamp */ + 118 | 4 * numPackets /* size prefix */; 119 | } 120 | 121 | inline size_t message(const String& address, size_t numArgs) 122 | { 123 | return string(address) + align(numArgs + 2); 124 | } 125 | 126 | template 127 | constexpr size_t message(char const (&address)[N], size_t numArgs) 128 | { 129 | return string(address) + align(numArgs + 2); 130 | } 131 | 132 | constexpr size_t int32(size_t n = 1) 133 | { 134 | return n * 4; 135 | } 136 | 137 | constexpr size_t float32(size_t n = 1) 138 | { 139 | return n * 4; 140 | } 141 | 142 | constexpr size_t float64(size_t n = 1) 143 | { 144 | return n * 8; 145 | } 146 | 147 | constexpr size_t string(size_t n) 148 | { 149 | return align(n + 1); 150 | } 151 | 152 | constexpr size_t blob(size_t size) 153 | { 154 | return 4 + align(size); 155 | } 156 | } // namespace Size 157 | } // namespace OSCPP 158 | 159 | #endif // OSCPP_UTIL_HPP_INCLUDED 160 | -------------------------------------------------------------------------------- /test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(warnings -Wall -Wextra -Wno-unused-parameter -Werror) 2 | 3 | add_compile_options( 4 | "$<$:${warnings}>" 5 | "$<$:${warnings}>" 6 | "$<$:${warnings}>" 7 | ) 8 | 9 | # ============================================================================= 10 | # autocheck tests 11 | 12 | add_executable(oscpp_autocheck 13 | oscpp_autocheck.cpp 14 | ) 15 | 16 | target_include_directories(oscpp_autocheck PRIVATE 17 | ../include 18 | autocheck/include 19 | ) 20 | 21 | add_test(oscpp_autocheck oscpp_autocheck) 22 | 23 | add_custom_command( 24 | OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/README.cpp 25 | COMMAND ruby ${CMAKE_CURRENT_SOURCE_DIR}/../tools/mdcode.rb 26 | ${CMAKE_CURRENT_SOURCE_DIR}/../README.md 27 | ${CMAKE_CURRENT_BINARY_DIR}/README.cpp 28 | MAIN_DEPENDENCY ${CMAKE_CURRENT_SOURCE_DIR}/../README.md 29 | ) 30 | 31 | # ============================================================================= 32 | # README code 33 | 34 | add_executable(oscpp_readme 35 | ${CMAKE_CURRENT_BINARY_DIR}/README.cpp 36 | ) 37 | 38 | target_include_directories(oscpp_readme PRIVATE 39 | ../include 40 | ) 41 | 42 | add_test(oscpp_readme oscpp_readme) 43 | -------------------------------------------------------------------------------- /test/oscpp_autocheck.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | namespace OSCPP { namespace AST { 12 | class Value 13 | { 14 | public: 15 | virtual ~Value() 16 | {} 17 | virtual void print(std::ostream& out) const = 0; 18 | virtual void put(OSCPP::Client::Packet& packet) const = 0; 19 | }; 20 | 21 | template using List = std::list>; 22 | 23 | template bool equalList(const List& list1, const List& list2) 24 | { 25 | if (list1.size() != list2.size()) 26 | return false; 27 | auto it1 = list1.begin(); 28 | auto it2 = list2.begin(); 29 | while ((it1 != list1.end()) && (it2 != list2.end())) 30 | { 31 | if (**it1 == **it2) 32 | { 33 | it1++; 34 | it2++; 35 | } 36 | else 37 | { 38 | return false; 39 | } 40 | } 41 | return true; 42 | } 43 | 44 | template void printList(std::ostream& out, const List& list) 45 | { 46 | const size_t n = list.size(); 47 | size_t i = 1; 48 | out << '['; 49 | for (auto x : list) 50 | { 51 | x->print(out); 52 | if (i != n) 53 | { 54 | out << ','; 55 | } 56 | i++; 57 | } 58 | out << ']'; 59 | } 60 | 61 | class Argument : public Value 62 | { 63 | public: 64 | enum Type 65 | { 66 | kInt32, 67 | kFloat32, 68 | kString, 69 | kBlob, 70 | kArray, 71 | }; 72 | static constexpr size_t kNumTypes = kArray + 1; 73 | 74 | Argument(Type type) 75 | : m_type(type) 76 | {} 77 | 78 | Type type() const 79 | { 80 | return m_type; 81 | } 82 | 83 | virtual size_t numTags() const 84 | { 85 | return 1; 86 | } 87 | 88 | static size_t numTags(const List& args) 89 | { 90 | size_t n = 0; 91 | for (auto x : args) 92 | n += x->numTags(); 93 | return n; 94 | } 95 | 96 | bool operator==(const Argument& other) 97 | { 98 | return (type() == other.type()) && equals(other); 99 | } 100 | 101 | virtual size_t size() const = 0; 102 | 103 | protected: 104 | virtual bool equals(const Argument& other) const = 0; 105 | 106 | private: 107 | Type m_type; 108 | }; 109 | 110 | class Bundle; 111 | class Message; 112 | 113 | class Packet : public Value 114 | { 115 | public: 116 | enum Type 117 | { 118 | kMessage, 119 | kBundle 120 | }; 121 | 122 | Packet(Type type) 123 | : m_type(type) 124 | {} 125 | 126 | Type type() const 127 | { 128 | return m_type; 129 | } 130 | 131 | virtual size_t size() const = 0; 132 | 133 | static std::shared_ptr parse(const OSCPP::Server::Packet& packet) 134 | { 135 | return packet.isBundle() ? parseBundle(packet) : parseMessage(packet); 136 | } 137 | 138 | bool operator==(const Packet& other) const 139 | { 140 | return type() == other.type() && equals(other); 141 | } 142 | 143 | protected: 144 | virtual bool equals(const Packet& other) const = 0; 145 | 146 | private: 147 | static std::shared_ptr 148 | parseBundle(const OSCPP::Server::Bundle& bdl); 149 | static void parseArgs(OSCPP::Server::ArgStream& inArgs, 150 | List& outArgs); 151 | static std::shared_ptr 152 | parseMessage(const OSCPP::Server::Message& msg); 153 | 154 | Type m_type; 155 | }; 156 | 157 | class Bundle : public Packet 158 | { 159 | public: 160 | Bundle(uint64_t time, List packets) 161 | : Packet(kBundle) 162 | , m_time(time) 163 | , m_packets(packets) 164 | { 165 | // assert(packets.size() > 0); 166 | } 167 | 168 | void print(std::ostream& out) const override 169 | { 170 | out << "Bundle(" << m_time << ", "; 171 | printList(out, m_packets); 172 | out << ')'; 173 | } 174 | 175 | void put(OSCPP::Client::Packet& packet) const override 176 | { 177 | packet.openBundle(m_time); 178 | for (auto p : m_packets) 179 | p->put(packet); 180 | packet.closeBundle(); 181 | } 182 | 183 | size_t size() const override 184 | { 185 | size_t payload = 0; 186 | for (auto x : m_packets) 187 | payload += x->size(); 188 | assert(OSCPP::isAligned(payload)); 189 | return OSCPP::Size::bundle(m_packets.size()) + payload; 190 | } 191 | 192 | protected: 193 | bool equals(const Packet& other) const override 194 | { 195 | const auto& otherBundle = dynamic_cast(other); 196 | return m_time == otherBundle.m_time && 197 | equalList(m_packets, otherBundle.m_packets); 198 | } 199 | 200 | private: 201 | uint64_t m_time; 202 | List m_packets; 203 | }; 204 | 205 | class Message : public Packet 206 | { 207 | public: 208 | Message(std::string address, List args) 209 | : Packet(kMessage) 210 | , m_address(address) 211 | , m_args(args) 212 | {} 213 | 214 | void print(std::ostream& out) const override 215 | { 216 | out << "Message(" << m_address << ", "; 217 | printList(out, m_args); 218 | out << ')'; 219 | } 220 | 221 | void put(OSCPP::Client::Packet& packet) const override 222 | { 223 | packet.openMessage(m_address.c_str(), Argument::numTags(m_args)); 224 | for (auto x : m_args) 225 | x->put(packet); 226 | packet.closeMessage(); 227 | } 228 | 229 | size_t size() const override 230 | { 231 | size_t payload = 0; 232 | for (auto x : m_args) 233 | payload += x->size(); 234 | assert(OSCPP::isAligned(payload)); 235 | return OSCPP::Size::message(OSCPP::Size::String(m_address.c_str()), 236 | Argument::numTags(m_args)) + 237 | payload; 238 | } 239 | 240 | protected: 241 | bool equals(const Packet& other) const override 242 | { 243 | const auto& otherMsg = dynamic_cast(other); 244 | return m_address == otherMsg.m_address && 245 | equalList(m_args, otherMsg.m_args); 246 | } 247 | 248 | private: 249 | std::string m_address; 250 | List m_args; 251 | }; 252 | 253 | class Int32 : public Argument 254 | { 255 | public: 256 | Int32(int32_t value) 257 | : Argument(kInt32) 258 | , m_value(value) 259 | {} 260 | 261 | void print(std::ostream& out) const override 262 | { 263 | out << "i:" << m_value; 264 | } 265 | 266 | void put(OSCPP::Client::Packet& packet) const override 267 | { 268 | packet.put(m_value); 269 | } 270 | 271 | size_t size() const override 272 | { 273 | return OSCPP::Size::int32(); 274 | } 275 | 276 | protected: 277 | bool equals(const Argument& other) const override 278 | { 279 | return dynamic_cast(other).m_value == m_value; 280 | } 281 | 282 | private: 283 | int32_t m_value; 284 | }; 285 | 286 | class Float32 : public Argument 287 | { 288 | public: 289 | Float32(float value) 290 | : Argument(kFloat32) 291 | , m_value(value) 292 | {} 293 | 294 | void print(std::ostream& out) const override 295 | { 296 | out << "f:" << m_value; 297 | } 298 | 299 | void put(OSCPP::Client::Packet& packet) const override 300 | { 301 | packet.put(m_value); 302 | } 303 | 304 | size_t size() const override 305 | { 306 | return OSCPP::Size::float32(); 307 | } 308 | 309 | protected: 310 | bool equals(const Argument& other) const override 311 | { 312 | return dynamic_cast(other).m_value == m_value; 313 | } 314 | 315 | private: 316 | float m_value; 317 | }; 318 | 319 | class String : public Argument 320 | { 321 | public: 322 | String(std::string value) 323 | : Argument(kString) 324 | , m_value(value) 325 | {} 326 | 327 | void print(std::ostream& out) const override 328 | { 329 | out << "s:" << m_value; 330 | } 331 | 332 | void put(OSCPP::Client::Packet& packet) const override 333 | { 334 | packet.put(m_value.c_str()); 335 | } 336 | 337 | size_t size() const override 338 | { 339 | return OSCPP::Size::string(OSCPP::Size::String(m_value.c_str())); 340 | } 341 | 342 | protected: 343 | bool equals(const Argument& other) const override 344 | { 345 | return dynamic_cast(other).m_value == m_value; 346 | } 347 | 348 | private: 349 | std::string m_value; 350 | }; 351 | 352 | class Blob : public Argument 353 | { 354 | public: 355 | Blob(int32_t size, const void* data = nullptr) 356 | : Argument(kBlob) 357 | , m_size(std::max(0, size)) 358 | , m_data(nullptr) 359 | { 360 | if (m_size > 0) 361 | { 362 | m_data = new char[m_size]; 363 | if (data != nullptr) 364 | std::memcpy(m_data, data, m_size); 365 | } 366 | } 367 | 368 | Blob(OSCPP::Blob b) 369 | : Blob(static_cast(b.size()), b.data()) 370 | {} 371 | 372 | ~Blob() 373 | { 374 | delete[] m_data; 375 | } 376 | 377 | void print(std::ostream& out) const override 378 | { 379 | out << "b:" << m_size; 380 | } 381 | 382 | void put(OSCPP::Client::Packet& packet) const override 383 | { 384 | packet.put(OSCPP::Blob(m_data, m_size)); 385 | } 386 | 387 | size_t size() const override 388 | { 389 | return OSCPP::Size::blob(m_size); 390 | } 391 | 392 | protected: 393 | bool equals(const Argument& other) const override 394 | { 395 | const Blob& otherBlob = dynamic_cast(other); 396 | return otherBlob.m_size == m_size && 397 | memcmp(m_data, otherBlob.m_data, m_size) == 0; 398 | } 399 | 400 | private: 401 | size_t m_size; 402 | char* m_data; 403 | }; 404 | 405 | class Array : public Argument 406 | { 407 | public: 408 | Array(List elems = List()) 409 | : Argument(kArray) 410 | , m_elems(elems) 411 | {} 412 | 413 | void print(std::ostream& out) const override 414 | { 415 | printList(out, m_elems); 416 | } 417 | 418 | void put(OSCPP::Client::Packet& packet) const override 419 | { 420 | packet.openArray(); 421 | for (auto x : m_elems) 422 | x->put(packet); 423 | packet.closeArray(); 424 | } 425 | 426 | size_t size() const override 427 | { 428 | size_t payload = 0; 429 | for (auto x : m_elems) 430 | payload += x->size(); 431 | assert(OSCPP::isAligned(payload)); 432 | return payload; 433 | } 434 | 435 | size_t numTags() const override 436 | { 437 | return OSCPP::Tags::array(Argument::numTags(m_elems)); 438 | } 439 | 440 | protected: 441 | bool equals(const Argument& other) const override 442 | { 443 | return equalList(m_elems, dynamic_cast(other).m_elems); 444 | } 445 | 446 | private: 447 | List m_elems; 448 | }; 449 | 450 | std::shared_ptr Packet::parseBundle(const OSCPP::Server::Bundle& bdl) 451 | { 452 | List outPackets; 453 | OSCPP::Server::PacketStream inPackets(bdl.packets()); 454 | while (!inPackets.atEnd()) 455 | { 456 | outPackets.push_back(parse(inPackets.next())); 457 | } 458 | return std::make_shared(bdl.time(), std::move(outPackets)); 459 | } 460 | 461 | void Packet::parseArgs(OSCPP::Server::ArgStream& inArgs, 462 | List& outArgs) 463 | { 464 | while (!inArgs.atEnd()) 465 | { 466 | switch (inArgs.tag()) 467 | { 468 | case 'i': 469 | outArgs.push_back(std::make_shared(inArgs.int32())); 470 | break; 471 | case 'f': 472 | outArgs.push_back(std::make_shared(inArgs.float32())); 473 | break; 474 | case 's': 475 | outArgs.push_back(std::make_shared(inArgs.string())); 476 | break; 477 | case 'b': 478 | outArgs.push_back(std::make_shared(inArgs.blob())); 479 | break; 480 | case '[': 481 | { 482 | OSCPP::Server::ArgStream inElems(inArgs.array()); 483 | List outElems; 484 | parseArgs(inElems, outElems); 485 | outArgs.push_back(std::make_shared(outElems)); 486 | } 487 | break; 488 | } 489 | } 490 | } 491 | 492 | std::shared_ptr Packet::parseMessage(const OSCPP::Server::Message& msg) 493 | { 494 | OSCPP::Server::ArgStream inArgs(msg.args()); 495 | List outArgs; 496 | parseArgs(inArgs, outArgs); 497 | return std::make_shared(msg.address(), outArgs); 498 | } 499 | 500 | std::ostream& operator<<(std::ostream& out, const Packet& packet) 501 | { 502 | packet.print(out); 503 | return out; 504 | } 505 | 506 | std::ostream& operator<<(std::ostream& out, 507 | const std::shared_ptr& packet) 508 | { 509 | packet->print(out); 510 | return out; 511 | } 512 | }} // namespace OSCPP::AST 513 | 514 | namespace ac = autocheck; 515 | 516 | namespace OSCPP { namespace AutoCheck { 517 | 518 | struct MessageArgListGen 519 | { 520 | typedef AST::List result_type; 521 | result_type operator()(size_t size) const; 522 | }; 523 | 524 | struct MessageArgGen 525 | { 526 | typedef std::shared_ptr result_type; 527 | result_type operator()(size_t size) const 528 | { 529 | AST::Argument::Type argType = static_cast( 530 | ac::generator()(AST::Argument::kNumTypes - 1)); 531 | switch (argType) 532 | { 533 | case AST::Argument::kInt32: 534 | return std::make_shared( 535 | ac::generator()(size)); 536 | case AST::Argument::kFloat32: 537 | return std::make_shared( 538 | ac::generator()(size)); 539 | case AST::Argument::kString: 540 | return std::make_shared( 541 | ac::string()(std::max(1, size))); 542 | case AST::Argument::kBlob: 543 | return std::make_shared( 544 | ac::generator()(size)); 545 | case AST::Argument::kArray: 546 | // Exponential size backoff 547 | return std::make_shared( 548 | MessageArgListGen()(size / 2)); 549 | default: 550 | throw std::logic_error("Invalid AST::Argument::Type value"); 551 | } 552 | const bool InvalidArgumentType = false; 553 | assert(InvalidArgumentType); 554 | } 555 | }; 556 | 557 | MessageArgListGen::result_type MessageArgListGen::operator()(size_t size) const 558 | { 559 | const auto& elems = ac::list_of(MessageArgGen())(size); 560 | return AST::List(elems.begin(), elems.end()); 561 | } 562 | 563 | struct PacketGen 564 | { 565 | // ac::generator> source; 566 | typedef std::shared_ptr result_type; 567 | result_type operator()(size_t size) const 568 | { 569 | return ac::generator()(size) ? gen_bundle(size) 570 | : gen_message(size); 571 | } 572 | 573 | result_type gen_bundle(size_t size) const 574 | { 575 | const auto& packets = ac::list_of(PacketGen())(size / 2); 576 | return std::make_shared( 577 | ac::generator()(size), 578 | AST::List(packets.begin(), packets.end())); 579 | } 580 | 581 | std::string gen_message_address(size_t size) const 582 | { 583 | std::string result( 584 | ac::string()(std::max(2, size))); 585 | if (result[0] != '/') 586 | result[0] = '/'; 587 | return result; 588 | } 589 | 590 | result_type gen_message(size_t size) const 591 | { 592 | return std::make_shared(gen_message_address(size), 593 | MessageArgListGen()(size)); 594 | } 595 | }; 596 | }} // namespace OSCPP::AutoCheck 597 | 598 | bool prop_identity(const std::shared_ptr& packet1) 599 | { 600 | // packet1->print(std::cerr); std::cerr << "\n"; 601 | const size_t size = packet1->size(); 602 | std::unique_ptr data(new char[size]); 603 | OSCPP::Client::Packet clientPacket(data.get(), size); 604 | packet1->put(clientPacket); 605 | OSCPP::Server::Packet serverPacket(clientPacket.data(), 606 | clientPacket.size()); 607 | auto packet2 = OSCPP::AST::Packet::parse(serverPacket); 608 | using namespace OSCPP::AST; 609 | if (!(*packet1 == *packet2)) 610 | { 611 | std::cerr << packet1 << std::endl; 612 | std::cerr << packet2 << std::endl; 613 | return false; 614 | } 615 | return true; 616 | } 617 | 618 | bool prop_overflow(const std::shared_ptr& packet, 619 | size_t inBufferSize) 620 | { 621 | const size_t packetSize = packet->size(); 622 | const size_t bufferSize = 623 | inBufferSize == 0 624 | ? 1 625 | : (inBufferSize < packetSize ? inBufferSize : packetSize - 1); 626 | std::cerr << "bufferSize " << bufferSize << std::endl; 627 | std::unique_ptr data(new char[bufferSize]); 628 | OSCPP::Client::Packet clientPacket(data.get(), bufferSize); 629 | bool result = false; 630 | try 631 | { 632 | packet->put(clientPacket); 633 | } 634 | catch (OSCPP::OverflowError&) 635 | { 636 | result = true; 637 | } 638 | catch (std::exception& e) 639 | { 640 | std::cerr << "Exception: " << e.what() << std::endl; 641 | } 642 | return result; 643 | } 644 | 645 | int main(int argc, char** argv) 646 | { 647 | using namespace OSCPP::AST; 648 | using namespace OSCPP::AutoCheck; 649 | ac::check>(prop_identity, 150, 650 | ac::make_arbitrary(PacketGen())); 651 | // ac::check,size_t>( 652 | // prop_overflow, 653 | // 150, 654 | // ac::make_arbitrary(PacketGen(), ac::generator()) 655 | // ); 656 | return 0; 657 | } 658 | -------------------------------------------------------------------------------- /tools/clang-format: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # clang-format all C/C++/ObjC source files under source control 3 | 4 | function source_files() 5 | { 6 | git ls-tree -r HEAD --name-only | grep -E '\.(h|hpp|cpp|m|M)$' 7 | } 8 | 9 | function clang_format() 10 | { 11 | xargs -n1 clang-format -style=file "$@" 12 | } 13 | 14 | case "$1" in 15 | check) 16 | source_files | clang_format -output-replacements-xml 17 | ;; 18 | *) 19 | source_files | clang_format -i 20 | ;; 21 | esac 22 | 23 | -------------------------------------------------------------------------------- /tools/mdcode.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | if ARGV.size != 2 4 | puts "Usage: #{File.basename($0)} INFILE OUTFILE" 5 | exit 1 6 | end 7 | 8 | cpp = false 9 | 10 | File.open(ARGV[1], "w") do |out| 11 | File.open(ARGV[0]).each do |line| 12 | if cpp 13 | if /^~~~~$/ =~ line 14 | cpp = false 15 | else 16 | out.write(line) 17 | end 18 | else 19 | if /^~~~~cpp$/ =~ line 20 | cpp = true 21 | end 22 | end 23 | end 24 | end 25 | --------------------------------------------------------------------------------