Multi User Server Client Linkage Environment v9.36

Open Source Software by Meyer Sound Laboratories Inc

Written by Jeremy Friesner, Last Updated 3/6/2024


Download the latest MUSCLE source and documentation (v9.36)

Go to the MUSCLE page on GitHub

Subscribe to the MUSCLE developer's mailing list

Watch a short video of a MUSCLE server synchronizing multiple clients in real time

Read the Beginner's Guide to MUSCLE

Take the self-guided learn-by-example API tour

Read the Guide to making a custom MUSCLE server

Demonstration of how to add multithreading to a custom MUSCLE server (v1.05)

Read the README file

Read the BeShare and MUSCLE BeNews article

MUSCLE is licensed under the BSD Open Source License

Study the MUSCLE API autodocs

Study the Java Client API Javadocs


What is MUSCLE?

MUSCLE is a robust, scalable, efficient, cross-platform client-server messaging system for dynamic distributed applications that runs under any POSIX-compliant operating system. MUSCLE has been developed, used, and refined as the networking component of BeShare, CueStation, D-Mitri, Spacemap Go, and various other audio control applications at Meyer Sound Laboratories for over twenty years. With MUSCLE, you can:


MUSCLE version history (recent releases only; for the full history see the History file in the muscle archive)

key:  - new feature
      * bug fixed
      o other

9.36 - Released 3/6/2024
   - Added GetMostRecentInputTimeStamp() and GetMostRecentOutputTimeStamp()
     methods to the AbstractReflectSession class
   - Added a GetMostRecentAcceptTimeStamp() method to the
     ReflectSessionFactory class.
   - Added a GetAcceptCount() method to the ReflectSessionFactory class.
   - PrintSessionsInfo() now includes text describing how recently
     each session has sent or received data.
   - PrintFactoriesInfo() now includes text describing how recently
     each factory has accepted an incoming TCP connection, and how
     many connections the factory has received so far.
   - Added a GetClientDescriptionString() virtual method to the
     AbstractReflectSession class to support more-useful debug output.
   - Added a GetSocketBindAddress() function to NetworkUtilityFunctions.h
     for querying where a socket is bound to on the local machine.
   o Renamed GetPeerIPAddress() to GetPeerAddress(), and changed it
     to return an IPAddressAndPort instead of returning an IPAddress
     and also using a separate (optPort) argument.
   o Tweaked zlib/zlib/zconf.h so that the gzlib files can build
     successfully even if ./configure wasn't called in the
     zlib/zlib folder first.
   o SocketMultiplexer::RegisterSocket() now emits an error message
     to the log if a file descriptor is too large to pass to FD_SET().
   * Updated build instructions to specify cmake instead of
     Visual Studio

9.35 - Released 2/14/2024
   - Added an optional (optRunAsUser) argument to the various
     process launch methods in ChildProcessDataIO.
   - Added convenience constructors to the IPAddress class
     that take an in_addr or in6_addr as an argument.
   - Added convenience constructors to the IPAddressAndPort class
     that take a sockaddr_in or sockaddr_in6 as an argument.
   - Added WriteToInAddr() and WriteToIn6Addr() convenience
     methods to the IPAddress class.
   - Added WriteToSockAddrIn() and WriteToSockAddrIn6()
     convenience methods to the IPAddressAndPort class.
   - Added convenience overloads of the SendDataUDP() and
     ReceiveDataUDP() functions that take an IPAddressAndPort
     as their source/destination argument.
   - Added method IPAddress::IsIPv6LinkLocal().
   o Changed ChildProcessDataIO::ChildProcessReadyToRun() to
     be protected instead of public.
   o Removed ip_address typedef; use IPAddress instead.
   o Tweaked Inet_NtoA() so that when called with the (preferIPv4)
     flag, it will return the invalid/all-zeroes IP address string
     as "" rather than "::".
   * Updated TarFileWriter to handle it a little more elegantly
     when a file's length changes while the tar data is being written
     out to a non-seekable DataIO.
   * Fixed a bug in the Ref class that would cause a NULL Ref's
     status-string to be returned as "OK" rather than "NULL Ref"
   * BitChord::SetByte()'s second argument was the wrong type.  Fixed.

9.34 - Released 12/18/2023
   - Added an optional localnicip=local.nic.ip.address argument
     to hexterm, to better support sending IPv4 multicast packets.
   - Added a B_SHUTTING_DOWN error code, useful for returning
     from Thread::MessageReceivedFromOwner() overrides if there
     isn't any real error but the internal thread is exiting on
     request.
   - Added a templated GetConstRefToDefaultObjectForType()
     convenience function to util/RefCount.h
   - Added a GetHardLinkCount() method to the FilePathInfo class.
   o Modified IsRegexToken() to return false for '-', since
     hyphens aren't meaningful except in the context of
     "character class" regex tokens (which will get escaped anyway).
   o Replaced the IsHosed() and SetHosed() methods of the
     AbstractMessageIOGateway class with more descriptive methods
     GetUnrecoverableErrorStatus() and SetUnrecoverableErrorStatus().

9.33 - Released 10/30/2023
   - Added FromHexString() and FromBinaryString() methods to the
     BitChord class, to complement the ToHexString()
     and ToBinaryString() methods.
   - Added a Queue::IsIndexValid(uint32) convenience-method.
   - Updated the Queue::Remove*() methods to use std::move()
     when possible.
   - Updated the Hashtable::Remove*() methods to use std::move()
     when possible.
   - Added IsAtStart() and IsAtEnd() convenience-methods to the
     HashtableIterator class.
   - The snoopsharedmem tool now support an optional (maxBytesToPrint)
     argument, in case you only want to see the header of the region.
   o Removed obsolete references to TARGET_PLATFORM_XENOMAI.
   o Updated the captive zlib library to the current GitHub version,
     to avoid "function declaration without a prototype is deprecated"
     compiler warnings.
   * Fixed the LogTime() and LogPlain() macros so that they compile
     when called from a different namespace.
   * Changed ImmutableHashtablePool::GetWithAux() to repurpose an
     existing table (if possible) while adding an item, as well
     as when removing one.

9.32 - Released 9/8/2023
   - Added a -DWITH_HELGRIND option to CMakeLists.txt to enable
     causality annotations in the reference-counting of RefCount.h
     so that helgrind doesn't give false-positive warnings when
     analyzing an execution of this code for race conditions.
   - Added MUSCLE_CONSTEXPR tag to trivial constructors and methods
     of various POD-like utility classes.
   - Added MUSCLE_CONSTEXPR_17 macro, for places where a constexpr
     tag is useful but only allowed in C++17 or later.
   o Updated PythonUtilityFunctions.h to use a forward-declaration
     instead of #include-ing Python.h directly, to avoid potential
     namespace-collisions between Python and Qt.
   * Applied fixes for various minor issues flagged by pvs-studio-analyzer.
   * Changed SignalMultiplexer's received-signal-counters to be
     AtomicCounters rather than uint32 to avoid helgrind complaints
     when a signal is handled.
   * The ObjectPool constructor now calls GetDefaultObjectForType()
     on its object-type and the associated Ref-types, to ensure that
     the static singleton is initialized while the process is still
     in a single-threaded state.
   * IsMessageDeflated() now takes a ConstMessageRef rather than MessageRef.
   * Fixed a pointer-type-mismatch issue that could cause unnecessary
     compile-time errors in the templated version of Hashtable::Remove()

9.31 - Released 6/14/2023
   - Added an IsFileOpen() method to the GZDataIO class.
   - Modified the Hashtable class so that HashtableIterators
     are only registered in the iterators-list if they are actually
     pointing at a valid HashtableEntry.
   - ParseConnectArg() and IPAddressAndPort::SetFromString() now support
     an alternate port-syntax that doesn't require brackets, to get around
     osascript's command-parsing limitations.  E.g. you can now specify
     "::1_port_1234" instead of "[::1]:1234"
   - Added convenience PODHashFunctor specializations (for QSize,
     QPoint, and QRect) to QMuscleSupport.h
   o Increased the Snooze64() time in testatomicvalue's writer thread
     to 10mS as it appears that 1mS snoozes were being rounded down to zero.
   o Added MUSCLE_NODISCARD tag to the String and IPAddressAndPort classes.
   o Increased the default value of AtomicValue's ATOMIC_BUFFER_SIZE
     template argument to 8.
   * Fixed code that was passing NULL as an argument to %s in printf() or
     LogTime().  Thanks to Ruurd Adema for pointing out that doing that is
     undefined behavior.
   * Changed Hashtable::_iteratorThreadID to be a std::atomic<> to
     avoid potential undefined behavior in multithreaded scenarios.
   * Fixed AtomicValue class to use bitmasks instead of modulo and
     only support powers-of-two array sizes.
   * Fixed the handling of InterlockedCompareExchange()'s return value
     in the AtomicValue class.

9.30 - Released 4/28/2023
   - Tagged the constructor of the MutexGuard class (and friends) as
     MUSCLE_NODISCARD so the compiler will warm about anonymous guards.
   - Tagged ObjectPool::ObtainObject() with MUSCLE_NODISCARD.
   - Added MUSCLE_NODISCARD to all the functions and methods that it
     wouldn't make sense to call without examining their return value.
   - Added MUSCLE_NODISCARD to various concrete classes (String,
     Queue, Hashtable, IPAddress, etc) that shouldn't be ignored when
     used as return values.
   o Renamed String::Append() to String::WithAppend().
   o Renamed String::Prepend() to String::WithPrepend().
   o Renamed String::AppendWord() to String::WithAppendedWord().
   o Renamed String::PrependWord() to String::WithPrependedWord().
   o Renamed String::Trim() to String::Trimmed().
   o Renamed String::Pad() to String::PaddedBy().
   o Renamed String::Indent() to String::IndentedBy().
   o Removed DEFAULT_MUSCLE_ROUTING_FLAGS_BIT_CHORD, and rewrote
     the routing flags to use the BitChord class.
   o Rewrote testnagle.cpp to include a unit-test for automated testing.
   o Updated function declarations in regex captive library to avoid
     compiler warnings.
   o Updated the unit tests so none of them takes more than 5 seconds
     to complete when done as part of a CMake "make test" pass.
   * Fixed logic error in testgateway.cpp.
   * MUSCLE_NODISCARD is now defined as a no-op when compiling C code,
     to avoid breaking the build under C compilers that say they support
     C23 but nevertheless don't grok [[nodiscard]] yet.

9.25 - Release 4/7/2023
   - Rewrote String::WithReplacements(const Hashtable<String,String> &
     to only scan the input string once, instead of once per key.
   - Added a String::Replace(const Hashtable<String,String> & method.
   - Added a ReplaceAllItems() convenience-method to the Queue class.
   - Added MUSCLE_NO_DISCARD keyword, and applied it to the Ref and
     ConstRef classes and to the status_t and io_status_t types, so that
     calls ignoring these return values will generate compile-time warnings.
   - Added a TruncateToLength() convenience method to the ByteBuffer class.
   - Added a PR_NAME_KEEPALIVE_INTERVAL_SECONDS parameter so clients can
     tell muscled to send a PR_RESULT_NOOP every (so many) seconds to
     verify TCP connectivity on an otherwise idle connection.
   o Removed DataIO::GetReadByteTimeStamp() as it wasn't being used.
   o CloseSocket() now prints an error message if its call to close() fails.
   * GetProcessMemoryUsage() now returns more accurate values.
   * Fixed a number of broken hyperlinks in the MUSCLE-by-example mkdocs.

9.24 - Release 3/20/2023
   - Added a CMakePresets.json file for quick specification of minimal
     (i.e. library-only) or full (i.e. including unit-tests) builds.
   - Added a CMAKEOPTIONS.txt file to describe the options available
     for building MUSCLE via cmake.
   o Moved the non-unit-test executables out of the "test" folder
     and into their own new/separate "tools" folder.
   o Renamed BUILDOPTIONS.txt to COMPILEROPTIONS.txt
   * Modified AbstractReflectSession::Reconnect() to not fail
     if CreateDefaultSocket() return a NULL reference.
   * The Python3 implementation of message.py wasn't encoding
     non-ASCII utf8 strings correctly when flattening a Message.  Fixed.
   * ZLibCodec::Inflate() would fail when inflating a compressed
     zero-byte buffer.  Fixed.

9.23 - Release 2/24/2023
   - Added a -DWITH_THREAD_SANITIZER=ON option to the CMakeLists.txt.
   - Added a -DMUSCLE_NUM_RESERVED_HIGH_BITS_IN_POINTERS flag to specify
     how many of the most-significant-bits of a pointer are reserved for
     system use.  This value defaults to 16 for 64-bit Android systems
     (to account for Android's MTE feature) and to 0 on everything else.
   - Added a _registeredSubscribersMutex to the ICallbackMechanism class
     so that it can handle unusual dispatching cases more gracefully.
   - Added a runtests.sh script to tests folder.  This script can be
     run to quickly execute all tests and report any detected regressions.
   - Added add_test() directives to test/CMakeLists.txt to enable CTest.
   o Renamed -DMUSCLE_AVOID_BITSTUFFING to -DMUSCLE_AVOID_TAGGED_POINTERS
     since the latter is a more commonly-used term.
   o Changed the default setting of the WITH_TESTS option to OFF
     in CMakeLists.txt
   * GetNetworkInterfaceinfos() is now implemented for Android
     (API level 24 or later).
   * SharedMemory.cpp now compiles under Android (pre API level 26)
     although its methods will return B_UNIMPLEMENTED if called.
   * GetNetworkInterfaceInfos() now returns B_UNIMPLEMENTED when
     called from an OS that it doesn't have an implementation for.
   * Applied some Android build fixes as suggested by Ruurd Adema.
   * SetFileLogLevel() was broken.  Fixed.
   * Avoid including sem.h when MUSCLE_FAKE_SHARED_MEMORY is defined.

9.22 - Released 2/2/2023
   - Modified Queue::InternalizeIndex() to use subtraction
     instead of modulo, for a 3x efficiency gain.
   - Added a MUSCLE_NOEXCEPT keyword that expands to noexcept
     under C++11 or later, or to nothing under C++03
   - Added MUSCLE_NOEXCEPT tags to the SwapContents() methods
     and move-constructors/operators that can make that guarantee.
   - Added a MatchesNodeNameQueryFilter class, to support query
     filtering against DataNode-names.
   - Added DataNode::SetNodeName()
   - Added a WITH_IPV6=OFF option to the CMakeLists.txt for
     easier building of MUSCLE in IPv4-only mode.
   - Added a "shareport" argument to hexterm to enable UDP port-sharing.
   - Added an IsBroadcast() method to the IPAddress class.
   - Added GetStatus() and SetStatus() methods to the
     Ref/ConstRef/DummyRef/DummyConstRef classes, so that when they
     are returned in a NULL state they can tell you why they aren't set.
   - Added BooleansToBitChord() convenience-functions to PointerAndBits.h
   - Updated PointerAndBits to be able to use the high bit in a
     pointer as well as the low bit(s).
   - Updated the captive zlib library to v1.2.13.
   o Replaced the PointerAndBool.h header (and class) with a new
     PointerAndBits.h header (and class) that does the same thing,
     but in a simpler, more generalized fashion.
   * Fixed broken UDP support in hexterm when -DMUSCLE_AVOID_IPV6
     was set as a compiler-flag.
   * Renamed SOCKET_FAMILY_IPV6 to SOCKET_FAMILY_IPV6_IS_DISABLED
     when -DMUSCLE_AVOID_IPV6 is set, to avoid potential runtime bugs.

9.21 - Released 1/13/2023
   - Added @tparam Doxygen tags for any template-arguments that
     the calling code might need to specify explicitly.
   - Enabled tagfile generation in muscle.dox.
   - Added a DataIO::ReadFullyUpTo() convenience-method to handle
     reading (up to n) bytes or (until EOF), whichever comes first.
   - Added error code B_END_OF_STREAM to allow Read()-type methods
     to unambiguously specify that the reason they are returning an
     error is because they have reached EOF/EOS.
   o Changed DataIO::ReadFully() and DataIO::WriteFully() to
     return status_t instead of uint32.
   o Removed the C APIs from the Doxygen output.
   o Auto-updated the muscle.dox file to the latest Doxygen version.
   * Fixed several issues to make Doxygen's output more useful.
   * Fixed some compiler warnings in the captive regex library.
   * Fixed the namespacing of the DoxyTemplates header so that
     Doxygen generation works with the newest Doxygen version.
   * Updated a number of out-of-date Doxygen comments.

9.20 - Released 12/30/2022
   - Updated NetworkUtilityFunctions.cpp to better support "real"
     IPv4 sockets (previously it assumed that IPv4 traffic would
     be handled using IPv6 sockets and IPv4-mapped IPv6 addresses,
     but that approach doesn't give 100% compatibility in all cases)
   - Added a GetSocketFamily() method to the Socket class.
   - Added an optional (socketFamily) argument to CreateUDPSocket().
   - Added IsOK(io_status_t &) and IsError(io_status_t &) method
     overrides to the io_status_t class.
   - hexterm now instantiates a genuine IPv4 socket for use with
     IPv4 multicast traffic.
   - Tweaked gz*.c includes to compile without a configuration step.
   - Added input-data-timestamping (including a PR_NAME_DATA_TIMESTAMP
     field and SetReceiveTimestampingEnabled() and
     GetReceiveTimestampingEnabled() methods) to the
     RawDataMessageIOGateway class.
   - Added SetLogLevelThreshold() and GetLogLevelThreshold() arguments
     to the LogCallback class so that any LogCallback can now specify
     what log-levels it is (or is not) interested in.
   - Added templated overloads of Message::FindFlat(), Message::FindTag()
     Message::GetFlat(), and Message::GetTag() that take any type of Ref
     as an argument, so calling code no longer has to pass in a generic
     RefCountableRef or FlatCountableRef and then do the necessary
     downcasting separately afterward.
   - Message::FindTag() now takes a ConstRefCountableRef as an
     argument rather than a RefCountableRef, for better flexibility.
   - Added templated overloads of Message::AddFlat(), PrependFlat(),
     ReplaceFlat(), AddTag(), PrependTag(), and ReplaceTag() so that
     the calling code no longer has to do manual upcasting of typed
     FlatCountableRef or RefCountableRef objects before calling them.
   - Added ConstMessageRef overloads of InflateMessage() and DeflateMessage().
   - Added a ConstMessageRef-returning overload of Message::FindMessage().
   o Removed the ByteBufferRef-specific *Flat() methods from
     the Message class, since we now have more general templatized
     methods that offer the same semantics for any type.
   o SocketMultiplexer::WaitForEvents() now returns an io_status_t
     rather than an int, for better error-reporting.
   o Merged the IPv6 and IPv4 versions of AddSocketToMulticastGroup()
     and RemoveSocketFromMulticastGroup() so that a single implementation
     can work on either type of socket.
   o Renamed SetSocketMulticastSendInterfaceAddress() and
     GetSocketMulticastSendInterfaceAddress() to
     SetIPv4SocketMulticastSendInterfaceAddress() and
     GetIPv4SocketMulticastSendInterfaceAddress(), respectively,
     and made them available even when -DMUSCLE_AVOID_IPV6 isn't set.
   o Renamed Log() to LogPlain().
   o Made LogTime() and LogPlain() into macros so that their arguments
     will not be evaluated unless the logging-threshold-test passes.
   o Improved error reporting in the ReflectServer event-loop.
   o Removed the functions from TimeUnitConversions.h that take
     (struct timeval &) as their argument, since they weren't being used.
   o Removed SetConsoleLogLevel() and GetConsoleLogLevel() from the
     DefaultConsoleLogger class, in favor of the new equivalent methods
     in the LogCallback base class.
   o Removed SetFileLogLevel() and GetFileLogLevel() from the
     DefaultFileLogger class, in favor of the new equivalent methods
     in the LogCallback base class.
   o Renamed muscle's private namespaces to muscle_private.
   o Replaced long with int32 in some I/O support functions.
   o The default ByteBufferPool now clears the IMemoryAllocationStrategy
     field of any ByteBuffer it recycles (after freeing its memory),
     to avoid polluting the pool with user-installed strategies.
   o Ref::SetFromRefCountableRef() now returns B_TYPE_MISMATCH on failure
     rather than B_BAD_ARGUMENT.
   o Removed the two-argument ConstRef and Ref "pseudo-constuctors"
     and added instead a DowncastTo() method, to
     make Ref-downcasting operations self-documenting.
   o Changed ITraversalPruner::MatchPath() and CreateObjectFromArchiveMessage()
     to take ConstMessageRef as an argument rather than MessageRef.
   o Changed StorageReflectSession::SetDataNode(), InsertOrderedData(),
     InsertOrderedChildNode(), and GetNewDataNode() to take
     ConstMessageRef as an argument rather than MessageRef.
   o Changed DataNode to hold a ConstMessageRef rather than a MessageRef.
   o Changed StorageReflectSession::NotifySubscribersThatNodeChanged()
     and StorageReflectSession::NodeChanged() to pass a ConstMessageRef
     rather than a MessageRef.
   o ByteBuffer::ReleaseBuffer() now returns (uint8 *) instead of
     (const uint8 *).
   * Fixed MLOG_ON_ERROR and friends to be usable with io_status_t.
   * ExpandLocalhostAddress() no longer expands IPv6 loopback addresses.
   * UDPSocketDataIO::ReadFrom() would call SetSourceOfLastReadPacket()
     even when no packet had been read, clearing that field.  Fixed.
   * Some functions in MuscleSupport.h were unintentionally being
     declared outside the muscle namespace.  Fixed.

9.10 - Released 12/10/2022
   - Rewrote the non-Windows implementation of muscleSprintf()
     to call vsnprintf() instead of sprintf(), to avoid compiler
     warnings about sprintf() being insecure.
   - Instrumented the non-Windows implementation of muscleSprintf()
     with MUSCLE_PRINTF_ARGS_ANNOTATION_PREFIX so that the compiler
     will warn about calls to muscleSprintf() with the wrong format
     specifiers.
   - Added an io_status_t class to represent the result of an I/O
     operation.  An io_status_t contains both a status_t and an
     int32 byte-count.
   - Added a MUSCLE-by-example page for the status_t class.
   - Added MTALLY_BYTES_OR_RETURN_ON_IO_ERROR() and
     MTALLY_BYTES_OR_RETURN_ON_IO_ERROR_OR_BREAK() macros to
     MuscleSupport.h.
   o Changed the return-types of the NetworkUtilityFunctions that
     previously returned (int32/byte-count-or-negative-1), aka
     SendData(), ReceiveData(), SendDataUDP(), ReceiveDataUDP(),
     ReadData(), and WriteData() to return a more informative
     io_status_t instead.
   o Changed the DataIO::Read() and DataIO::Write() methods to
     return io_status_t instead of int32.
   o Changed the PacketDataIO::ReadFrom() and PacketDataIO::WriteTo()
     methods to return io_status_t instead of int32.
   o Simplified the implementation of GetDefaultObjectForType() back
     to one that compile under any version of C++.  We'll rely on
     the optimizer to do the right thing, rather than SFINAE.
   o Modified Directory::SetDir() to return a static status_t value
     rather than B_ERRNO, to avoid potential static-initialization
     ordering problems on pre-C++11 compilers.
   o tagged status_t as a MUSCLE_FINAL_CLASS.
   o Replaced the MAKETYPE(x) macro with a MakeWhatCode() function.
   * Updated message_transceiver_thread.py to catch and ignore any
     EAGAIN exceptions generated by send() on a notification-socket.
   * Fixed a spurious assertion failure in SimulatedMulticastDataIO.
   * Changed some code to return B_IO_ERROR instead of B_ERRNO if
     fread() or fwrite() fails, since those functions are not
     guaranteed to set errno when they fail.
   * Fixed a bug that would cause GetEnvironmentVariableValue() to
     sometimes return garbage strings under Windows if the environment
     variable did not exist.
   * Added missing DOxygen parameter documentation for various
     macros declared in MuscleSupport.h.
   * Fixed a number of broken hyperlinks in the MUSCLE-by-example docs.

9.01 - Released 11/18/2022
   - Added a WritePaddingBytesToAlignTo(uint32 alignSize)
     convenience-method to the CheckedDataFlattenerHelper
     and DataFlattenerHelper classes.
   - Added a SeekPastPaddingBytesToAlignTo(uint32 alignSize)
     convenience-method to the DataUnflattenerHelper class.
   - Added DataUnflattener ctor and SetBuffer() calls that
     take a ByteBufferRef, for convenience.
   - Added a GetEnvironmentVariableValue() convenience function,
     to avoid calling getenv() directly from user code.
   - Added %p (aka process ID) to the set of tokens expanded by
     HumanReadableTimeValues::ExpandTokens().
   - DefaultFileLogger::EnsureLogFileCreated() now tries a little
     harder: if it can't create a log file with the given file name,
     it will try up to 10 variants of the file name in the hopes
     of coming up with a file name that is unique and can be created.
   - Added a IsBitIndexValid() convenience-method to the BitChord class.
   - Added MUSCLE_CONSTEXPR_OR_CONST macro to expand to "constexpr"
     if possible, or "const" otherwise.
   - GetDefaultObjectForType() now uses constexpr to avoid on-demand
     initialization, when -DMUSCLE_USE_CPLUSPLUS17 is defined.
   - CMakeLists.txt now automatically specifies -DMUSCLE_USE_PTHREADS
     if building on POSIX for C++03, and WITH_THREADS is ON.
   - LogTime() calls now emit printf-style format-usage warnings
     when compiled with g++ or clang++.
   - Added INT16_FORMAT_SPEC_* macros, for completeness.
   o PODSwapper now calls std::swap() rather than reimplementing it.
   * Moved the definitions of the B_* error-codes out of SetupSystem.cpp
     and into MuscleSupport.h, so that Clang's Static Analyzer can see
     what values they contain and generate better output.
   * Generation of the random number that %r expands to is now
     actually random (not just calling rand()) (in C++11 or newer).
   * Renamed several of the longer mkdocs examples folder names
     to avoid hitting Windows' 260-character path limit.
   * Fixed a potential uninitialized-memory-read in the String
     class's GetLevenshteinDistance() method.
   * Fixed the CMakeList.txt files in the sub-folders to use
     the inherited CMAKE_CXX_STANDARD setting rather than
     forcing C++11.
   * GetCurrentThreadID() is no longer compiled if
     -DMUSCLE_SINGLE_THREAD_ONLY is specified.

9.00 - Released 10/27/2022
   - Added a MUSCLE_MAXIMUM_NODE_DEPTH constant that limits
     how deep the MUSCLE node-tree may become, to prevent
     stack-overflow attacks.  Defaults to 100.
   - Added DataFlattener and DataUnflattener classes to
     allow for safer flattening/unflattening of data
     to/from byte-buffers.
   - Added an UncheckedDataUnflattener class, for better
     efficiency when the calling code has already done its
     own bounds-checking.
   - Added a CheckedDataUnflattener class, for better
     calling code that wants to use a dynamically-growing
     output buffer.
   - Added support/EndianConverter.h to allow templating
     over big/little/native endian-encoding strategies.
   - Added pages for DataFlattener and DataUnflattener
     APIs to the muscle-by-example documentation.
   - Adding -DMUSCLE_USE_BIG_ENDIAN_DATA_FOR_EVERYTHING
     to your compile line will tell MUSCLE to use big-endian
     data format for all of its data.  Note that doing so will
     break interoperability with all existing MUSCLE builds!
   - Adding -DMUSCLE_USE_NATIVE_ENDIAN_DATA_FOR_EVERYTHING
     to your compile line will tell MUSCLE to use native-endian
     data format for all of its data.  Note that doing so will
     break compatibility with different-endian CPUs!
   o Changed the PseudoFlattenable::Flatten() and
     Flattenable::Flatten() methods to take a second
     argument (flatSize) for better runtime error-checking.
   o Changed the PseudoFlattenable::Unflatten() and
     Flattenable::Unflatten() methods to take a
     (DataUnflattener &) as an argument rather than
     a raw pointer, for better caller/callee cooperation.
   o Changed Message::TemplatedFlatten() to use
     a DataFlattener as an argument.
   o Changed Message::TemplatedUnflatten() to use
     a DataUnflattener as an argument.
   o Removed the SetEndianFlag(), Append*(), Write*(),
     Read*() methods from the ByteBuffer class.  Use
     the new DataFlattener/DataUnflattener classes instead.
   o Refactored the StringTokenizer class's constructors to
     take a single (optSepChars) argument instead of separate
     arguments for hard and soft separator characters.
   o Made the calculation of DataNode depths more efficient.
   o Removed Flattenable::WriteData() and Flattenable::ReadData()
     methods (since DataFlattener/DataUnflattener do it better).
   o Replaced most calls to B_LENDIAN_*_TO_HOST() and
     B_HOST_TO_LENDIAN_*() with calls to DefaultEndianConverter's
     Import() and Export() methods, to allow MUSCLE to be built
     in big-endian mode.
   o Changed the PseudoFlattenable class to be templatized, and
     added methods to it so all the helper methods declared in
     the Flattenable interface are also available in the
     PseudoFlattenable interface.
   o Refactored the ZLib-support classes in muscle/zlib so that they
     no longer include any zlib-headers from their header files.
   * Modified Snooze64() to call clock_nanosleep() under Linux,
     and to call nanosleep() when -DMUSCLE_USE_LIBRT is defined.
   * Changed all the #include "zlib/zlib/zlib.h" directives to
     #include "zlib.h" to avoid using the captive zlib headers
     together with any system-supplied zlib implementation (which
     might be different)
   * Fixed a bug that could cause ZLibDataIO::WriteAux() to
     to into an infinite recursion if zlib's deflate() errored out.
   * Fixed a bug in MessageIOGateway that could cause it fail
     to report how many bytes it had sent or received, if an
     I/O error occurred later on in the same call to DoInput()
     or DoOutput().