The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1195 lines
44KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2020 - Raw Material Software Limited
  5. JUCE is an open source library subject to commercial or open-source
  6. licensing.
  7. By using JUCE, you agree to the terms of both the JUCE 6 End-User License
  8. Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
  9. End User License Agreement: www.juce.com/juce-6-licence
  10. Privacy Policy: www.juce.com/juce-privacy-policy
  11. Or: You may also use this code under the terms of the GPL v3 (see
  12. www.gnu.org/licenses).
  13. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
  14. EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
  15. DISCLAIMED.
  16. ==============================================================================
  17. */
  18. namespace juce
  19. {
  20. namespace
  21. {
  22. //==============================================================================
  23. /** Allows a block of data to be accessed as a stream of OSC data.
  24. The memory is shared and will be neither copied nor owned by the OSCInputStream.
  25. This class is implementing the Open Sound Control 1.0 Specification for
  26. interpreting the data.
  27. Note: Some older implementations of OSC may omit the OSC Type Tag string
  28. in OSC messages. This class will treat such OSC messages as format errors.
  29. */
  30. class OSCInputStream
  31. {
  32. public:
  33. /** Creates an OSCInputStream.
  34. @param sourceData the block of data to use as the stream's source
  35. @param sourceDataSize the number of bytes in the source data block
  36. */
  37. OSCInputStream (const void* sourceData, size_t sourceDataSize)
  38. : input (sourceData, sourceDataSize, false)
  39. {}
  40. //==============================================================================
  41. /** Returns a pointer to the source data block from which this stream is reading. */
  42. const void* getData() const noexcept { return input.getData(); }
  43. /** Returns the number of bytes of source data in the block from which this stream is reading. */
  44. size_t getDataSize() const noexcept { return input.getDataSize(); }
  45. /** Returns the current position of the stream. */
  46. uint64 getPosition() { return (uint64) input.getPosition(); }
  47. /** Attempts to set the current position of the stream. Returns true if this was successful. */
  48. bool setPosition (int64 pos) { return input.setPosition (pos); }
  49. /** Returns the total amount of data in bytes accessible by this stream. */
  50. int64 getTotalLength() { return input.getTotalLength(); }
  51. /** Returns true if the stream has no more data to read. */
  52. bool isExhausted() { return input.isExhausted(); }
  53. //==============================================================================
  54. int32 readInt32()
  55. {
  56. checkBytesAvailable (4, "OSC input stream exhausted while reading int32");
  57. return input.readIntBigEndian();
  58. }
  59. uint64 readUint64()
  60. {
  61. checkBytesAvailable (8, "OSC input stream exhausted while reading uint64");
  62. return (uint64) input.readInt64BigEndian();
  63. }
  64. float readFloat32()
  65. {
  66. checkBytesAvailable (4, "OSC input stream exhausted while reading float");
  67. return input.readFloatBigEndian();
  68. }
  69. String readString()
  70. {
  71. checkBytesAvailable (4, "OSC input stream exhausted while reading string");
  72. auto posBegin = (size_t) getPosition();
  73. auto s = input.readString();
  74. auto posEnd = (size_t) getPosition();
  75. if (static_cast<const char*> (getData()) [posEnd - 1] != '\0')
  76. throw OSCFormatError ("OSC input stream exhausted before finding null terminator of string");
  77. size_t bytesRead = posEnd - posBegin;
  78. readPaddingZeros (bytesRead);
  79. return s;
  80. }
  81. MemoryBlock readBlob()
  82. {
  83. checkBytesAvailable (4, "OSC input stream exhausted while reading blob");
  84. auto blobDataSize = input.readIntBigEndian();
  85. checkBytesAvailable ((blobDataSize + 3) % 4, "OSC input stream exhausted before reaching end of blob");
  86. MemoryBlock blob;
  87. auto bytesRead = input.readIntoMemoryBlock (blob, (ssize_t) blobDataSize);
  88. readPaddingZeros (bytesRead);
  89. return blob;
  90. }
  91. OSCColour readColour()
  92. {
  93. checkBytesAvailable (4, "OSC input stream exhausted while reading colour");
  94. return OSCColour::fromInt32 ((uint32) input.readIntBigEndian());
  95. }
  96. OSCTimeTag readTimeTag()
  97. {
  98. checkBytesAvailable (8, "OSC input stream exhausted while reading time tag");
  99. return OSCTimeTag (uint64 (input.readInt64BigEndian()));
  100. }
  101. OSCAddress readAddress()
  102. {
  103. return OSCAddress (readString());
  104. }
  105. OSCAddressPattern readAddressPattern()
  106. {
  107. return OSCAddressPattern (readString());
  108. }
  109. //==============================================================================
  110. OSCTypeList readTypeTagString()
  111. {
  112. OSCTypeList typeList;
  113. checkBytesAvailable (4, "OSC input stream exhausted while reading type tag string");
  114. if (input.readByte() != ',')
  115. throw OSCFormatError ("OSC input stream format error: expected type tag string");
  116. for (;;)
  117. {
  118. if (isExhausted())
  119. throw OSCFormatError ("OSC input stream exhausted while reading type tag string");
  120. const OSCType type = input.readByte();
  121. if (type == 0)
  122. break; // encountered null terminator. list is complete.
  123. if (! OSCTypes::isSupportedType (type))
  124. throw OSCFormatError ("OSC input stream format error: encountered unsupported type tag");
  125. typeList.add (type);
  126. }
  127. auto bytesRead = (size_t) typeList.size() + 2;
  128. readPaddingZeros (bytesRead);
  129. return typeList;
  130. }
  131. //==============================================================================
  132. OSCArgument readArgument (OSCType type)
  133. {
  134. switch (type)
  135. {
  136. case OSCTypes::int32: return OSCArgument (readInt32());
  137. case OSCTypes::float32: return OSCArgument (readFloat32());
  138. case OSCTypes::string: return OSCArgument (readString());
  139. case OSCTypes::blob: return OSCArgument (readBlob());
  140. case OSCTypes::colour: return OSCArgument (readColour());
  141. default:
  142. // You supplied an invalid OSCType when calling readArgument! This should never happen.
  143. jassertfalse;
  144. throw OSCInternalError ("OSC input stream: internal error while reading message argument");
  145. }
  146. }
  147. //==============================================================================
  148. OSCMessage readMessage()
  149. {
  150. auto ap = readAddressPattern();
  151. auto types = readTypeTagString();
  152. OSCMessage msg (ap);
  153. for (auto& type : types)
  154. msg.addArgument (readArgument (type));
  155. return msg;
  156. }
  157. //==============================================================================
  158. OSCBundle readBundle (size_t maxBytesToRead = std::numeric_limits<size_t>::max())
  159. {
  160. // maxBytesToRead is only passed in here in case this bundle is a nested
  161. // bundle, so we know when to consider the next element *not* part of this
  162. // bundle anymore (but part of the outer bundle) and return.
  163. checkBytesAvailable (16, "OSC input stream exhausted while reading bundle");
  164. if (readString() != "#bundle")
  165. throw OSCFormatError ("OSC input stream format error: bundle does not start with string '#bundle'");
  166. OSCBundle bundle (readTimeTag());
  167. size_t bytesRead = 16; // already read "#bundle" and timeTag
  168. auto pos = getPosition();
  169. while (! isExhausted() && bytesRead < maxBytesToRead)
  170. {
  171. bundle.addElement (readElement());
  172. auto newPos = getPosition();
  173. bytesRead += (size_t) (newPos - pos);
  174. pos = newPos;
  175. }
  176. return bundle;
  177. }
  178. //==============================================================================
  179. OSCBundle::Element readElement()
  180. {
  181. checkBytesAvailable (4, "OSC input stream exhausted while reading bundle element size");
  182. auto elementSize = (size_t) readInt32();
  183. if (elementSize < 4)
  184. throw OSCFormatError ("OSC input stream format error: invalid bundle element size");
  185. return readElementWithKnownSize (elementSize);
  186. }
  187. //==============================================================================
  188. OSCBundle::Element readElementWithKnownSize (size_t elementSize)
  189. {
  190. checkBytesAvailable ((int64) elementSize, "OSC input stream exhausted while reading bundle element content");
  191. auto firstContentChar = static_cast<const char*> (getData()) [getPosition()];
  192. if (firstContentChar == '/') return OSCBundle::Element (readMessageWithCheckedSize (elementSize));
  193. if (firstContentChar == '#') return OSCBundle::Element (readBundleWithCheckedSize (elementSize));
  194. throw OSCFormatError ("OSC input stream: invalid bundle element content");
  195. }
  196. private:
  197. MemoryInputStream input;
  198. //==============================================================================
  199. void readPaddingZeros (size_t bytesRead)
  200. {
  201. size_t numZeros = ~(bytesRead - 1) & 0x03;
  202. while (numZeros > 0)
  203. {
  204. if (isExhausted() || input.readByte() != 0)
  205. throw OSCFormatError ("OSC input stream format error: missing padding zeros");
  206. --numZeros;
  207. }
  208. }
  209. OSCBundle readBundleWithCheckedSize (size_t size)
  210. {
  211. auto begin = (size_t) getPosition();
  212. auto maxBytesToRead = size - 4; // we've already read 4 bytes (the bundle size)
  213. OSCBundle bundle (readBundle (maxBytesToRead));
  214. if (getPosition() - begin != size)
  215. throw OSCFormatError ("OSC input stream format error: wrong element content size encountered while reading");
  216. return bundle;
  217. }
  218. OSCMessage readMessageWithCheckedSize (size_t size)
  219. {
  220. auto begin = (size_t) getPosition();
  221. auto message = readMessage();
  222. if (getPosition() - begin != size)
  223. throw OSCFormatError ("OSC input stream format error: wrong element content size encountered while reading");
  224. return message;
  225. }
  226. void checkBytesAvailable (int64 requiredBytes, const char* message)
  227. {
  228. if (input.getNumBytesRemaining() < requiredBytes)
  229. throw OSCFormatError (message);
  230. }
  231. };
  232. } // namespace
  233. //==============================================================================
  234. struct OSCReceiver::Pimpl : private Thread,
  235. private MessageListener
  236. {
  237. Pimpl (const String& oscThreadName) : Thread (oscThreadName)
  238. {
  239. }
  240. ~Pimpl() override
  241. {
  242. disconnect();
  243. }
  244. //==============================================================================
  245. bool connectToPort (int portNumber)
  246. {
  247. if (! disconnect())
  248. return false;
  249. socket.setOwned (new DatagramSocket (false));
  250. if (! socket->bindToPort (portNumber))
  251. return false;
  252. startThread();
  253. return true;
  254. }
  255. bool connectToSocket (DatagramSocket& newSocket)
  256. {
  257. if (! disconnect())
  258. return false;
  259. socket.setNonOwned (&newSocket);
  260. startThread();
  261. return true;
  262. }
  263. bool disconnect()
  264. {
  265. if (socket != nullptr)
  266. {
  267. signalThreadShouldExit();
  268. if (socket.willDeleteObject())
  269. socket->shutdown();
  270. waitForThreadToExit (10000);
  271. socket.reset();
  272. }
  273. return true;
  274. }
  275. //==============================================================================
  276. void addListener (OSCReceiver::Listener<MessageLoopCallback>* listenerToAdd)
  277. {
  278. listeners.add (listenerToAdd);
  279. }
  280. void addListener (OSCReceiver::Listener<RealtimeCallback>* listenerToAdd)
  281. {
  282. realtimeListeners.add (listenerToAdd);
  283. }
  284. void addListener (ListenerWithOSCAddress<MessageLoopCallback>* listenerToAdd,
  285. OSCAddress addressToMatch)
  286. {
  287. addListenerWithAddress (listenerToAdd, addressToMatch, listenersWithAddress);
  288. }
  289. void addListener (ListenerWithOSCAddress<RealtimeCallback>* listenerToAdd, OSCAddress addressToMatch)
  290. {
  291. addListenerWithAddress (listenerToAdd, addressToMatch, realtimeListenersWithAddress);
  292. }
  293. void removeListener (OSCReceiver::Listener<MessageLoopCallback>* listenerToRemove)
  294. {
  295. listeners.remove (listenerToRemove);
  296. }
  297. void removeListener (OSCReceiver::Listener<RealtimeCallback>* listenerToRemove)
  298. {
  299. realtimeListeners.remove (listenerToRemove);
  300. }
  301. void removeListener (ListenerWithOSCAddress<MessageLoopCallback>* listenerToRemove)
  302. {
  303. removeListenerWithAddress (listenerToRemove, listenersWithAddress);
  304. }
  305. void removeListener (ListenerWithOSCAddress<RealtimeCallback>* listenerToRemove)
  306. {
  307. removeListenerWithAddress (listenerToRemove, realtimeListenersWithAddress);
  308. }
  309. //==============================================================================
  310. struct CallbackMessage : public Message
  311. {
  312. CallbackMessage (OSCBundle::Element oscElement) : content (oscElement) {}
  313. // the payload of the message. Can be either an OSCMessage or an OSCBundle.
  314. OSCBundle::Element content;
  315. };
  316. //==============================================================================
  317. void handleBuffer (const char* data, size_t dataSize)
  318. {
  319. OSCInputStream inStream (data, dataSize);
  320. try
  321. {
  322. auto content = inStream.readElementWithKnownSize (dataSize);
  323. // realtime listeners should receive the OSC content first - and immediately
  324. // on this thread:
  325. callRealtimeListeners (content);
  326. if (content.isMessage())
  327. callRealtimeListenersWithAddress (content.getMessage());
  328. // now post the message that will trigger the handleMessage callback
  329. // dealing with the non-realtime listeners.
  330. if (listeners.size() > 0 || listenersWithAddress.size() > 0)
  331. postMessage (new CallbackMessage (content));
  332. }
  333. catch (const OSCFormatError&)
  334. {
  335. if (formatErrorHandler != nullptr)
  336. formatErrorHandler (data, (int) dataSize);
  337. }
  338. }
  339. //==============================================================================
  340. void registerFormatErrorHandler (OSCReceiver::FormatErrorHandler handler)
  341. {
  342. formatErrorHandler = handler;
  343. }
  344. private:
  345. //==============================================================================
  346. void run() override
  347. {
  348. int bufferSize = 65535;
  349. HeapBlock<char> oscBuffer (bufferSize);
  350. while (! threadShouldExit())
  351. {
  352. jassert (socket != nullptr);
  353. auto ready = socket->waitUntilReady (true, 100);
  354. if (ready < 0 || threadShouldExit())
  355. return;
  356. if (ready == 0)
  357. continue;
  358. auto bytesRead = (size_t) socket->read (oscBuffer.getData(), bufferSize, false);
  359. if (bytesRead >= 4)
  360. handleBuffer (oscBuffer.getData(), bytesRead);
  361. }
  362. }
  363. //==============================================================================
  364. template <typename ListenerType>
  365. void addListenerWithAddress (ListenerType* listenerToAdd,
  366. OSCAddress address,
  367. Array<std::pair<OSCAddress, ListenerType*>>& array)
  368. {
  369. for (auto& i : array)
  370. if (address == i.first && listenerToAdd == i.second)
  371. return;
  372. array.add (std::make_pair (address, listenerToAdd));
  373. }
  374. //==============================================================================
  375. template <typename ListenerType>
  376. void removeListenerWithAddress (ListenerType* listenerToRemove,
  377. Array<std::pair<OSCAddress, ListenerType*>>& array)
  378. {
  379. for (int i = 0; i < array.size(); ++i)
  380. {
  381. if (listenerToRemove == array.getReference (i).second)
  382. {
  383. // aarrgh... can't simply call array.remove (i) because this
  384. // requires a default c'tor to be present for OSCAddress...
  385. // luckily, we don't care about methods preserving element order:
  386. array.swap (i, array.size() - 1);
  387. array.removeLast();
  388. break;
  389. }
  390. }
  391. }
  392. //==============================================================================
  393. void handleMessage (const Message& msg) override
  394. {
  395. if (auto* callbackMessage = dynamic_cast<const CallbackMessage*> (&msg))
  396. {
  397. auto& content = callbackMessage->content;
  398. callListeners (content);
  399. if (content.isMessage())
  400. callListenersWithAddress (content.getMessage());
  401. }
  402. }
  403. //==============================================================================
  404. void callListeners (const OSCBundle::Element& content)
  405. {
  406. using OSCListener = OSCReceiver::Listener<OSCReceiver::MessageLoopCallback>;
  407. if (content.isMessage())
  408. {
  409. auto&& message = content.getMessage();
  410. listeners.call ([&] (OSCListener& l) { l.oscMessageReceived (message); });
  411. }
  412. else if (content.isBundle())
  413. {
  414. auto&& bundle = content.getBundle();
  415. listeners.call ([&] (OSCListener& l) { l.oscBundleReceived (bundle); });
  416. }
  417. }
  418. void callRealtimeListeners (const OSCBundle::Element& content)
  419. {
  420. using OSCListener = OSCReceiver::Listener<OSCReceiver::RealtimeCallback>;
  421. if (content.isMessage())
  422. {
  423. auto&& message = content.getMessage();
  424. realtimeListeners.call ([&] (OSCListener& l) { l.oscMessageReceived (message); });
  425. }
  426. else if (content.isBundle())
  427. {
  428. auto&& bundle = content.getBundle();
  429. realtimeListeners.call ([&] (OSCListener& l) { l.oscBundleReceived (bundle); });
  430. }
  431. }
  432. //==============================================================================
  433. void callListenersWithAddress (const OSCMessage& message)
  434. {
  435. for (auto& entry : listenersWithAddress)
  436. if (auto* listener = entry.second)
  437. if (message.getAddressPattern().matches (entry.first))
  438. listener->oscMessageReceived (message);
  439. }
  440. void callRealtimeListenersWithAddress (const OSCMessage& message)
  441. {
  442. for (auto& entry : realtimeListenersWithAddress)
  443. if (auto* listener = entry.second)
  444. if (message.getAddressPattern().matches (entry.first))
  445. listener->oscMessageReceived (message);
  446. }
  447. //==============================================================================
  448. ListenerList<OSCReceiver::Listener<OSCReceiver::MessageLoopCallback>> listeners;
  449. ListenerList<OSCReceiver::Listener<OSCReceiver::RealtimeCallback>> realtimeListeners;
  450. Array<std::pair<OSCAddress, OSCReceiver::ListenerWithOSCAddress<OSCReceiver::MessageLoopCallback>*>> listenersWithAddress;
  451. Array<std::pair<OSCAddress, OSCReceiver::ListenerWithOSCAddress<OSCReceiver::RealtimeCallback>*>> realtimeListenersWithAddress;
  452. OptionalScopedPointer<DatagramSocket> socket;
  453. OSCReceiver::FormatErrorHandler formatErrorHandler { nullptr };
  454. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Pimpl)
  455. };
  456. //==============================================================================
  457. OSCReceiver::OSCReceiver (const String& threadName) : pimpl (new Pimpl (threadName))
  458. {
  459. }
  460. OSCReceiver::OSCReceiver() : OSCReceiver ("JUCE OSC server")
  461. {
  462. }
  463. OSCReceiver::~OSCReceiver()
  464. {
  465. pimpl.reset();
  466. }
  467. bool OSCReceiver::connect (int portNumber)
  468. {
  469. return pimpl->connectToPort (portNumber);
  470. }
  471. bool OSCReceiver::connectToSocket (DatagramSocket& socket)
  472. {
  473. return pimpl->connectToSocket (socket);
  474. }
  475. bool OSCReceiver::disconnect()
  476. {
  477. return pimpl->disconnect();
  478. }
  479. void OSCReceiver::addListener (OSCReceiver::Listener<MessageLoopCallback>* listenerToAdd)
  480. {
  481. pimpl->addListener (listenerToAdd);
  482. }
  483. void OSCReceiver::addListener (Listener<RealtimeCallback>* listenerToAdd)
  484. {
  485. pimpl->addListener (listenerToAdd);
  486. }
  487. void OSCReceiver::addListener (ListenerWithOSCAddress<MessageLoopCallback>* listenerToAdd, OSCAddress addressToMatch)
  488. {
  489. pimpl->addListener (listenerToAdd, addressToMatch);
  490. }
  491. void OSCReceiver::addListener (ListenerWithOSCAddress<RealtimeCallback>* listenerToAdd, OSCAddress addressToMatch)
  492. {
  493. pimpl->addListener (listenerToAdd, addressToMatch);
  494. }
  495. void OSCReceiver::removeListener (Listener<MessageLoopCallback>* listenerToRemove)
  496. {
  497. pimpl->removeListener (listenerToRemove);
  498. }
  499. void OSCReceiver::removeListener (Listener<RealtimeCallback>* listenerToRemove)
  500. {
  501. pimpl->removeListener (listenerToRemove);
  502. }
  503. void OSCReceiver::removeListener (ListenerWithOSCAddress<MessageLoopCallback>* listenerToRemove)
  504. {
  505. pimpl->removeListener (listenerToRemove);
  506. }
  507. void OSCReceiver::removeListener (ListenerWithOSCAddress<RealtimeCallback>* listenerToRemove)
  508. {
  509. pimpl->removeListener (listenerToRemove);
  510. }
  511. void OSCReceiver::registerFormatErrorHandler (FormatErrorHandler handler)
  512. {
  513. pimpl->registerFormatErrorHandler (handler);
  514. }
  515. //==============================================================================
  516. //==============================================================================
  517. #if JUCE_UNIT_TESTS
  518. class OSCInputStreamTests : public UnitTest
  519. {
  520. public:
  521. OSCInputStreamTests()
  522. : UnitTest ("OSCInputStream class", UnitTestCategories::osc)
  523. {}
  524. void runTest()
  525. {
  526. beginTest ("reading OSC addresses");
  527. {
  528. const char buffer[16] = {
  529. '/', 't', 'e', 's', 't', '/', 'f', 'a',
  530. 'd', 'e', 'r', '7', '\0', '\0', '\0', '\0' };
  531. // reading a valid osc address:
  532. {
  533. OSCInputStream inStream (buffer, sizeof (buffer));
  534. OSCAddress address = inStream.readAddress();
  535. expect (inStream.getPosition() == sizeof (buffer));
  536. expectEquals (address.toString(), String ("/test/fader7"));
  537. }
  538. // check various possible failures:
  539. {
  540. // zero padding is present, but size is not modulo 4:
  541. OSCInputStream inStream (buffer, 15);
  542. expectThrowsType (inStream.readAddress(), OSCFormatError)
  543. }
  544. {
  545. // zero padding is missing:
  546. OSCInputStream inStream (buffer, 12);
  547. expectThrowsType (inStream.readAddress(), OSCFormatError)
  548. }
  549. {
  550. // pattern does not start with a forward slash:
  551. OSCInputStream inStream (buffer + 4, 12);
  552. expectThrowsType (inStream.readAddress(), OSCFormatError)
  553. }
  554. }
  555. beginTest ("reading OSC address patterns");
  556. {
  557. const char buffer[20] = {
  558. '/', '*', '/', '*', 'p', 'u', 't', '/',
  559. 'f', 'a', 'd', 'e', 'r', '[', '0', '-',
  560. '9', ']', '\0', '\0' };
  561. // reading a valid osc address pattern:
  562. {
  563. OSCInputStream inStream (buffer, sizeof (buffer));
  564. expectDoesNotThrow (inStream.readAddressPattern());
  565. }
  566. {
  567. OSCInputStream inStream (buffer, sizeof (buffer));
  568. OSCAddressPattern ap = inStream.readAddressPattern();
  569. expect (inStream.getPosition() == sizeof (buffer));
  570. expectEquals (ap.toString(), String ("/*/*put/fader[0-9]"));
  571. expect (ap.containsWildcards());
  572. }
  573. // check various possible failures:
  574. {
  575. // zero padding is present, but size is not modulo 4:
  576. OSCInputStream inStream (buffer, 19);
  577. expectThrowsType (inStream.readAddressPattern(), OSCFormatError)
  578. }
  579. {
  580. // zero padding is missing:
  581. OSCInputStream inStream (buffer, 16);
  582. expectThrowsType (inStream.readAddressPattern(), OSCFormatError)
  583. }
  584. {
  585. // pattern does not start with a forward slash:
  586. OSCInputStream inStream (buffer + 4, 16);
  587. expectThrowsType (inStream.readAddressPattern(), OSCFormatError)
  588. }
  589. }
  590. beginTest ("reading OSC time tags");
  591. {
  592. char buffer[8] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 };
  593. OSCInputStream inStream (buffer, sizeof (buffer));
  594. OSCTimeTag tag = inStream.readTimeTag();
  595. expect (tag.isImmediately());
  596. }
  597. {
  598. char buffer[8] = { 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
  599. OSCInputStream inStream (buffer, sizeof (buffer));
  600. OSCTimeTag tag = inStream.readTimeTag();
  601. expect (! tag.isImmediately());
  602. }
  603. beginTest ("reading OSC arguments");
  604. {
  605. // test data:
  606. int testInt = -2015;
  607. const uint8 testIntRepresentation[] = { 0xFF, 0xFF, 0xF8, 0x21 }; // big endian two's complement
  608. float testFloat = 345.6125f;
  609. const uint8 testFloatRepresentation[] = { 0x43, 0xAC, 0xCE, 0x66 }; // big endian IEEE 754
  610. String testString = "Hello, World!";
  611. const char testStringRepresentation[] = {
  612. 'H', 'e', 'l', 'l', 'o', ',', ' ', 'W',
  613. 'o', 'r', 'l', 'd', '!', '\0', '\0', '\0' }; // padded to size % 4 == 0
  614. const uint8 testBlobData[] = { 0xBB, 0xCC, 0xDD, 0xEE, 0xFF };
  615. const MemoryBlock testBlob (testBlobData, sizeof (testBlobData));
  616. const uint8 testBlobRepresentation[] = {
  617. 0x00, 0x00, 0x00, 0x05,
  618. 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, 0x00, 0x00, 0x00 }; // size prefixed + padded to size % 4 == 0
  619. // read:
  620. {
  621. {
  622. // int32:
  623. OSCInputStream inStream (testIntRepresentation, sizeof (testIntRepresentation));
  624. OSCArgument arg = inStream.readArgument (OSCTypes::int32);
  625. expect (inStream.getPosition() == 4);
  626. expect (arg.isInt32());
  627. expectEquals (arg.getInt32(), testInt);
  628. }
  629. {
  630. // float32:
  631. OSCInputStream inStream (testFloatRepresentation, sizeof (testFloatRepresentation));
  632. OSCArgument arg = inStream.readArgument (OSCTypes::float32);
  633. expect (inStream.getPosition() == 4);
  634. expect (arg.isFloat32());
  635. expectEquals (arg.getFloat32(), testFloat);
  636. }
  637. {
  638. // string:
  639. OSCInputStream inStream (testStringRepresentation, sizeof (testStringRepresentation));
  640. OSCArgument arg = inStream.readArgument (OSCTypes::string);
  641. expect (inStream.getPosition() == sizeof (testStringRepresentation));
  642. expect (arg.isString());
  643. expectEquals (arg.getString(), testString);
  644. }
  645. {
  646. // blob:
  647. OSCInputStream inStream (testBlobRepresentation, sizeof (testBlobRepresentation));
  648. OSCArgument arg = inStream.readArgument (OSCTypes::blob);
  649. expect (inStream.getPosition() == sizeof (testBlobRepresentation));
  650. expect (arg.isBlob());
  651. expect (arg.getBlob() == testBlob);
  652. }
  653. }
  654. // read invalid representations:
  655. {
  656. // not enough bytes
  657. {
  658. const uint8 rawData[] = { 0xF8, 0x21 };
  659. OSCInputStream inStream (rawData, sizeof (rawData));
  660. expectThrowsType (inStream.readArgument (OSCTypes::int32), OSCFormatError);
  661. expectThrowsType (inStream.readArgument (OSCTypes::float32), OSCFormatError);
  662. }
  663. // test string not being padded to multiple of 4 bytes:
  664. {
  665. const char rawData[] = {
  666. 'H', 'e', 'l', 'l', 'o', ',', ' ', 'W',
  667. 'o', 'r', 'l', 'd', '!', '\0' }; // padding missing
  668. OSCInputStream inStream (rawData, sizeof (rawData));
  669. expectThrowsType (inStream.readArgument (OSCTypes::string), OSCFormatError);
  670. }
  671. {
  672. const char rawData[] = {
  673. 'H', 'e', 'l', 'l', 'o', ',', ' ', 'W',
  674. 'o', 'r', 'l', 'd', '!', '\0', 'x', 'x' }; // padding with non-zero chars
  675. OSCInputStream inStream (rawData, sizeof (rawData));
  676. expectThrowsType (inStream.readArgument (OSCTypes::string), OSCFormatError);
  677. }
  678. // test blob not being padded to multiple of 4 bytes:
  679. {
  680. const uint8 rawData[] = { 0x00, 0x00, 0x00, 0x05, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF }; // padding missing
  681. OSCInputStream inStream (rawData, sizeof (rawData));
  682. expectThrowsType (inStream.readArgument (OSCTypes::blob), OSCFormatError);
  683. }
  684. // test blob having wrong size
  685. {
  686. const uint8 rawData[] = { 0x00, 0x00, 0x00, 0x12, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF };
  687. OSCInputStream inStream (rawData, sizeof (rawData));
  688. expectThrowsType (inStream.readArgument (OSCTypes::blob), OSCFormatError);
  689. }
  690. }
  691. }
  692. beginTest ("reading OSC messages (type tag string)");
  693. {
  694. {
  695. // valid empty message
  696. const char data[] = {
  697. '/', 't', 'e', 's', 't', '\0', '\0', '\0',
  698. ',', '\0', '\0', '\0' };
  699. OSCInputStream inStream (data, sizeof (data));
  700. auto msg = inStream.readMessage();
  701. expect (msg.getAddressPattern().toString() == "/test");
  702. expect (msg.size() == 0);
  703. }
  704. {
  705. // invalid message: no type tag string
  706. const char data[] = {
  707. '/', 't', 'e', 's', 't', '\0', '\0', '\0',
  708. 'H', 'e', 'l', 'l', 'o', ',', ' ', 'W',
  709. 'o', 'r', 'l', 'd', '!', '\0', '\0', '\0' };
  710. OSCInputStream inStream (data, sizeof (data));
  711. expectThrowsType (inStream.readMessage(), OSCFormatError);
  712. }
  713. {
  714. // invalid message: no type tag string and also empty
  715. const char data[] = { '/', 't', 'e', 's', 't', '\0', '\0', '\0' };
  716. OSCInputStream inStream (data, sizeof (data));
  717. expectThrowsType (inStream.readMessage(), OSCFormatError);
  718. }
  719. // invalid message: wrong padding
  720. {
  721. const char data[] = { '/', 't', 'e', 's', 't', '\0', '\0', '\0', ',', '\0', '\0', '\0' };
  722. OSCInputStream inStream (data, sizeof (data) - 1);
  723. expectThrowsType (inStream.readMessage(), OSCFormatError);
  724. }
  725. // invalid message: says it contains an arg, but doesn't
  726. {
  727. const char data[] = { '/', 't', 'e', 's', 't', '\0', '\0', '\0', ',', 'i', '\0', '\0' };
  728. OSCInputStream inStream (data, sizeof (data));
  729. expectThrowsType (inStream.readMessage(), OSCFormatError);
  730. }
  731. // invalid message: binary size does not match size deducted from type tag string
  732. {
  733. const char data[] = { '/', 't', 'e', 's', 't', '\0', '\0', '\0', ',', 'i', 'f', '\0' };
  734. OSCInputStream inStream (data, sizeof (data));
  735. expectThrowsType (inStream.readMessage(), OSCFormatError);
  736. }
  737. }
  738. beginTest ("reading OSC messages (contents)");
  739. {
  740. // valid non-empty message.
  741. {
  742. int32 testInt = -2015;
  743. float testFloat = 345.6125f;
  744. String testString = "Hello, World!";
  745. const uint8 testBlobData[] = { 0xBB, 0xCC, 0xDD, 0xEE, 0xFF };
  746. const MemoryBlock testBlob (testBlobData, sizeof (testBlobData));
  747. uint8 data[] = {
  748. '/', 't', 'e', 's', 't', '\0', '\0', '\0',
  749. ',', 'i', 'f', 's', 'b', '\0', '\0', '\0',
  750. 0xFF, 0xFF, 0xF8, 0x21,
  751. 0x43, 0xAC, 0xCE, 0x66,
  752. 'H', 'e', 'l', 'l', 'o', ',', ' ', 'W', 'o', 'r', 'l', 'd', '!', '\0', '\0', '\0',
  753. 0x00, 0x00, 0x00, 0x05, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, 0x00, 0x00, 0x00
  754. };
  755. OSCInputStream inStream (data, sizeof (data));
  756. auto msg = inStream.readMessage();
  757. expectEquals (msg.getAddressPattern().toString(), String ("/test"));
  758. expectEquals (msg.size(), 4);
  759. expectEquals (msg[0].getType(), OSCTypes::int32);
  760. expectEquals (msg[1].getType(), OSCTypes::float32);
  761. expectEquals (msg[2].getType(), OSCTypes::string);
  762. expectEquals (msg[3].getType(), OSCTypes::blob);
  763. expectEquals (msg[0].getInt32(), testInt);
  764. expectEquals (msg[1].getFloat32(), testFloat);
  765. expectEquals (msg[2].getString(), testString);
  766. expect (msg[3].getBlob() == testBlob);
  767. }
  768. }
  769. beginTest ("reading OSC messages (handling of corrupted messages)");
  770. {
  771. // invalid messages
  772. {
  773. OSCInputStream inStream (nullptr, 0);
  774. expectThrowsType (inStream.readMessage(), OSCFormatError);
  775. }
  776. {
  777. const uint8 data[] = { 0x00 };
  778. OSCInputStream inStream (data, 0);
  779. expectThrowsType (inStream.readMessage(), OSCFormatError);
  780. }
  781. {
  782. uint8 data[] = {
  783. '/', 't', 'e', 's', 't', '\0', '\0', '\0',
  784. ',', 'i', 'f', 's', 'b', // type tag string not padded
  785. 0xFF, 0xFF, 0xF8, 0x21,
  786. 0x43, 0xAC, 0xCE, 0x66,
  787. 'H', 'e', 'l', 'l', 'o', ',', ' ', 'W', 'o', 'r', 'l', 'd', '!', '\0', '\0', '\0',
  788. 0x00, 0x00, 0x00, 0x05, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, 0x00, 0x00, 0x00
  789. };
  790. OSCInputStream inStream (data, sizeof (data));
  791. expectThrowsType (inStream.readMessage(), OSCFormatError);
  792. }
  793. {
  794. uint8 data[] = {
  795. '/', 't', 'e', 's', 't', '\0', '\0', '\0',
  796. ',', 'i', 'f', 's', 'b', '\0', '\0', '\0',
  797. 0xFF, 0xFF, 0xF8, 0x21,
  798. 0x43, 0xAC, 0xCE, 0x66,
  799. 'H', 'e', 'l', 'l', 'o', ',', ' ', 'W', 'o', 'r', 'l', 'd', '!', '\0', '\0', '\0' // rest of message cut off
  800. };
  801. OSCInputStream inStream (data, sizeof (data));
  802. expectThrowsType (inStream.readMessage(), OSCFormatError);
  803. }
  804. }
  805. beginTest ("reading OSC messages (handling messages without type tag strings)");
  806. {
  807. {
  808. uint8 data[] = { '/', 't', 'e', 's', 't', '\0', '\0', '\0' };
  809. OSCInputStream inStream (data, sizeof (data));
  810. expectThrowsType (inStream.readMessage(), OSCFormatError);
  811. }
  812. {
  813. uint8 data[] = {
  814. '/', 't', 'e', 's', 't', '\0', '\0', '\0',
  815. 0xFF, 0xFF, 0xF8, 0x21,
  816. 0x43, 0xAC, 0xCE, 0x66,
  817. 'H', 'e', 'l', 'l', 'o', ',', ' ', 'W', 'o', 'r', 'l', 'd', '!', '\0', '\0', '\0',
  818. 0x00, 0x00, 0x00, 0x05, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, 0x00, 0x00, 0x00
  819. };
  820. OSCInputStream inStream (data, sizeof (data));
  821. expectThrowsType (inStream.readMessage(), OSCFormatError);
  822. }
  823. }
  824. beginTest ("reading OSC bundles");
  825. {
  826. // valid bundle (empty)
  827. {
  828. uint8 data[] = {
  829. '#', 'b', 'u', 'n', 'd', 'l', 'e', '\0',
  830. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01
  831. };
  832. OSCInputStream inStream (data, sizeof (data));
  833. OSCBundle bundle = inStream.readBundle();
  834. expect (bundle.getTimeTag().isImmediately());
  835. expect (bundle.size() == 0);
  836. }
  837. // valid bundle (containing both messages and other bundles)
  838. {
  839. int32 testInt = -2015;
  840. float testFloat = 345.6125f;
  841. String testString = "Hello, World!";
  842. const uint8 testBlobData[] = { 0xBB, 0xCC, 0xDD, 0xEE, 0xFF };
  843. const MemoryBlock testBlob (testBlobData, sizeof (testBlobData));
  844. uint8 data[] = {
  845. '#', 'b', 'u', 'n', 'd', 'l', 'e', '\0',
  846. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
  847. 0x00, 0x00, 0x00, 0x34,
  848. '/', 't', 'e', 's', 't', '/', '1', '\0',
  849. ',', 'i', 'f', 's', 'b', '\0', '\0', '\0',
  850. 0xFF, 0xFF, 0xF8, 0x21,
  851. 0x43, 0xAC, 0xCE, 0x66,
  852. 'H', 'e', 'l', 'l', 'o', ',', ' ', 'W', 'o', 'r', 'l', 'd', '!', '\0', '\0', '\0',
  853. 0x00, 0x00, 0x00, 0x05, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, 0x00, 0x00, 0x00,
  854. 0x00, 0x00, 0x00, 0x0C,
  855. '/', 't', 'e', 's', 't', '/', '2', '\0',
  856. ',', '\0', '\0', '\0',
  857. 0x00, 0x00, 0x00, 0x10,
  858. '#', 'b', 'u', 'n', 'd', 'l', 'e', '\0',
  859. 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
  860. };
  861. OSCInputStream inStream (data, sizeof (data));
  862. OSCBundle bundle = inStream.readBundle();
  863. expect (bundle.getTimeTag().isImmediately());
  864. expect (bundle.size() == 3);
  865. OSCBundle::Element* elements = bundle.begin();
  866. expect (elements[0].isMessage());
  867. expect (elements[0].getMessage().getAddressPattern().toString() == "/test/1");
  868. expect (elements[0].getMessage().size() == 4);
  869. expect (elements[0].getMessage()[0].isInt32());
  870. expect (elements[0].getMessage()[1].isFloat32());
  871. expect (elements[0].getMessage()[2].isString());
  872. expect (elements[0].getMessage()[3].isBlob());
  873. expectEquals (elements[0].getMessage()[0].getInt32(), testInt);
  874. expectEquals (elements[0].getMessage()[1].getFloat32(), testFloat);
  875. expectEquals (elements[0].getMessage()[2].getString(), testString);
  876. expect (elements[0].getMessage()[3].getBlob() == testBlob);
  877. expect (elements[1].isMessage());
  878. expect (elements[1].getMessage().getAddressPattern().toString() == "/test/2");
  879. expect (elements[1].getMessage().size() == 0);
  880. expect (elements[2].isBundle());
  881. expect (! elements[2].getBundle().getTimeTag().isImmediately());
  882. }
  883. // invalid bundles.
  884. {
  885. uint8 data[] = {
  886. '#', 'b', 'u', 'n', 'd', 'l', 'e', '\0',
  887. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
  888. 0x00, 0x00, 0x00, 0x34, // wrong bundle element size (too large)
  889. '/', 't', 'e', 's', 't', '/', '1', '\0',
  890. ',', 's', '\0', '\0',
  891. 'H', 'e', 'l', 'l', 'o', ',', ' ', 'W', 'o', 'r', 'l', 'd', '!', '\0', '\0', '\0'
  892. };
  893. OSCInputStream inStream (data, sizeof (data));
  894. expectThrowsType (inStream.readBundle(), OSCFormatError);
  895. }
  896. {
  897. uint8 data[] = {
  898. '#', 'b', 'u', 'n', 'd', 'l', 'e', '\0',
  899. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
  900. 0x00, 0x00, 0x00, 0x08, // wrong bundle element size (too small)
  901. '/', 't', 'e', 's', 't', '/', '1', '\0',
  902. ',', 's', '\0', '\0',
  903. 'H', 'e', 'l', 'l', 'o', ',', ' ', 'W', 'o', 'r', 'l', 'd', '!', '\0', '\0', '\0'
  904. };
  905. OSCInputStream inStream (data, sizeof (data));
  906. expectThrowsType (inStream.readBundle(), OSCFormatError);
  907. }
  908. {
  909. uint8 data[] = {
  910. '#', 'b', 'u', 'n', 'd', 'l', 'e', '\0',
  911. 0x00, 0x00, 0x00, 0x00 // incomplete time tag
  912. };
  913. OSCInputStream inStream (data, sizeof (data));
  914. expectThrowsType (inStream.readBundle(), OSCFormatError);
  915. }
  916. {
  917. uint8 data[] = {
  918. '#', 'b', 'u', 'n', 'x', 'l', 'e', '\0', // wrong initial string
  919. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
  920. };
  921. OSCInputStream inStream (data, sizeof (data));
  922. expectThrowsType (inStream.readBundle(), OSCFormatError);
  923. }
  924. {
  925. uint8 data[] = {
  926. '#', 'b', 'u', 'n', 'd', 'l', 'e', // padding missing from string
  927. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
  928. };
  929. OSCInputStream inStream (data, sizeof (data));
  930. expectThrowsType (inStream.readBundle(), OSCFormatError);
  931. }
  932. }
  933. }
  934. };
  935. static OSCInputStreamTests OSCInputStreamUnitTests;
  936. #endif
  937. } // namespace juce