new cspot/bell

This commit is contained in:
philippe44
2023-05-06 23:50:26 +02:00
parent e0e7e718ba
commit 8bad480112
163 changed files with 6611 additions and 6739 deletions

View File

@@ -3,29 +3,41 @@
#include <functional> // for function
#include <memory> // for shared_ptr
#include <string> // for string
#include <atomic>
namespace bell {
class WrappedSemaphore;
};
namespace cspot {
struct Context;
class AccessKeyFetcher {
public:
AccessKeyFetcher(std::shared_ptr<cspot::Context> ctx);
~AccessKeyFetcher();
typedef std::function<void(std::string)> Callback;
/**
* @brief Checks if key is expired
* @returns true when currently held access key is not valid
*/
bool isExpired();
void getAccessKey(Callback callback);
/**
* @brief Fetches a new access key
* @remark In case the key is expired, this function blocks until a refresh is done.
* @returns access key
*/
std::string getAccessKey();
/**
* @brief Forces a refresh of the access key
*/
void updateAccessKey();
private:
const std::string CLIENT_ID =
"65b708073fc0480ea92a077233ca87bd"; // Spotify web client's client id
const std::string SCOPES =
"streaming,user-library-read,user-library-modify,user-top-read,user-read-"
"recently-played"; // Required access scopes
std::shared_ptr<cspot::Context> ctx;
std::shared_ptr<bell::WrappedSemaphore> updateSemaphore;
bool isExpired();
std::atomic<bool> keyPending = false;
std::string accessKey;
long long int expiresAt;
};

View File

@@ -8,17 +8,16 @@
namespace cspot {
class ApResolve {
private:
std::string apOverride;
public:
ApResolve(std::string apOverride);
/**
* @brief Connects to spotify's servers and returns first valid ap address
*
* @return std::string Address in form of url:port
*/
* @brief Connects to spotify's servers and returns first valid ap address
* @returns std::string Address in form of url:port
*/
std::string fetchFirstApAddress();
private:
std::string apOverride;
};
} // namespace cspot

View File

@@ -1,10 +1,9 @@
#pragma once
#include <cstdint> // for uint8_t
#include <memory> // for unique_ptr
#include <string> // for string
#include <vector> // for vector
#include <cstdint> // for uint8_t
#include <memory> // for unique_ptr
#include <string> // for string
#include <vector> // for vector
#include "Crypto.h" // for Crypto
#include "protobuf/authentication.pb.h" // for ClientResponseEncrypted
@@ -16,20 +15,45 @@ class AuthChallenges {
AuthChallenges();
~AuthChallenges();
std::vector<uint8_t> shanSendKey = {};
std::vector<uint8_t> shanRecvKey = {};
/**
* @brief Prepares a spotify authentication packet
* @param authBlob authentication blob bytes
* @param authType value representing spotify's authentication type
* @param deviceId device id to use during auth.
* @param username spotify's username
*
* @returns vector containing bytes of the authentication packet
*/
std::vector<uint8_t> prepareAuthPacket(std::vector<uint8_t>& authBlob,
int authType,
const std::string& deviceId,
const std::string& username);
/**
* @brief Solves the ApHello packet, and returns a packet with response
*
* @param helloPacket hello packet bytes received from the server
* @param data authentication data received from the server
*
* @returns vector containing response packet
*/
std::vector<uint8_t> solveApHello(std::vector<uint8_t>& helloPacket,
std::vector<uint8_t>& data);
/**
* @brief Prepares an client hello packet, used for initial auth with spotify
*
* @returns vector containing the packet's data
*/
std::vector<uint8_t> prepareClientHello();
std::vector<uint8_t> shanSendKey = {};
std::vector<uint8_t> shanRecvKey = {};
private:
const long long SPOTIFY_VERSION = 0x10800000000;
// Protobuf structures
ClientResponseEncrypted authRequest;
ClientResponsePlaintext clientResPlaintext;
ClientHello clientHello;
@@ -37,4 +61,4 @@ class AuthChallenges {
std::unique_ptr<Crypto> crypto;
};
} // namespace cspot
} // namespace cspot

View File

@@ -2,10 +2,10 @@
#include <memory>
#include "LoginBlob.h"
#include "MercurySession.h"
#include "TimeProvider.h"
#include "protobuf/metadata.pb.h"
#include "LoginBlob.h"
namespace cspot {
struct Context {
@@ -25,7 +25,8 @@ struct Context {
std::shared_ptr<TimeProvider> timeProvider;
std::shared_ptr<cspot::MercurySession> session;
static std::shared_ptr<Context> createFromBlob(std::shared_ptr<LoginBlob> blob) {
static std::shared_ptr<Context> createFromBlob(
std::shared_ptr<LoginBlob> blob) {
auto ctx = std::make_shared<Context>();
ctx->timeProvider = std::make_shared<TimeProvider>();
@@ -37,6 +38,5 @@ struct Context {
return ctx;
}
};
} // namespace cspot
} // namespace cspot

View File

@@ -7,11 +7,11 @@ extern char deviceId[];
namespace cspot {
// Hardcoded information sent to spotify servers
const char * const informationString = "cspot-player";
const char * const brandName = "cspot";
const char * const versionString = "cspot-1.1";
const char * const protocolVersion = "2.7.1";
const char * const defaultDeviceName = "CSpot";
const char * const swVersion = "1.0.0";
const char* const informationString = "cspot-player";
const char* const brandName = "cspot";
const char* const versionString = "cspot-1.1";
const char* const protocolVersion = "2.7.1";
const char* const defaultDeviceName = "CSpot";
const char* const swVersion = "1.0.0";
}
} // namespace cspot

View File

@@ -3,14 +3,13 @@
#include <stdio.h>
#include <cassert>
#define CSPOT_ASSERT(CONDITION, MESSAGE) \
do \
{ \
if (!(CONDITION)) \
{ \
printf("At %s in %s:%d\n Assertion %s failed: %s", __func__, __FILE__, __LINE__, #CONDITION, MESSAGE); \
abort(); \
} \
} while (0)
#define CSPOT_ASSERT(CONDITION, MESSAGE) \
do { \
if (!(CONDITION)) { \
printf("At %s in %s:%d\n Assertion %s failed: %s", __func__, __FILE__, \
__LINE__, #CONDITION, MESSAGE); \
abort(); \
} \
} while (0)
#endif

View File

@@ -2,8 +2,7 @@
#include <BellLogger.h>
#define CSPOT_LOG(type, ...) \
do \
{ \
bell::bellGlobalLogger->type(__FILE__, __LINE__, "cspot", __VA_ARGS__); \
} while (0)
#define CSPOT_LOG(type, ...) \
do { \
bell::bellGlobalLogger->type(__FILE__, __LINE__, "cspot", __VA_ARGS__); \
} while (0)

View File

@@ -1,10 +1,10 @@
#pragma once
#include <cstdint> // for uint8_t, uint32_t
#include <map> // for map
#include <memory> // for unique_ptr
#include <string> // for string
#include <vector> // for vector
#include <cstdint> // for uint8_t, uint32_t
#include <map> // for map
#include <memory> // for unique_ptr
#include <string> // for string
#include <vector> // for vector
#include "Crypto.h" // for CryptoMbedTLS, Crypto

View File

@@ -1,13 +1,13 @@
#pragma once
#include <atomic> // for atomic
#include <cstdint> // for uint8_t, uint64_t, uint32_t
#include <functional> // for function
#include <memory> // for shared_ptr
#include <mutex> // for mutex
#include <string> // for string
#include <unordered_map> // for unordered_map
#include <vector> // for vector
#include <atomic> // for atomic
#include <cstdint> // for uint8_t, uint64_t, uint32_t
#include <functional> // for function
#include <memory> // for shared_ptr
#include <mutex> // for mutex
#include <string> // for string
#include <unordered_map> // for unordered_map
#include <vector> // for vector
#include "BellTask.h" // for Task
#include "Packet.h" // for Packet
@@ -15,7 +15,7 @@
#include "Session.h" // for Session
#include "protobuf/mercury.pb.h" // for Header
namespace cspot {
namespace cspot {
class TimeProvider;
class MercurySession : public bell::Task, public cspot::Session {
@@ -33,7 +33,8 @@ class MercurySession : public bell::Task, public cspot::Session {
};
typedef std::function<void(Response&)> ResponseCallback;
typedef std::function<void(bool, const std::vector<uint8_t>&)> AudioKeyCallback;
typedef std::function<void(bool, const std::vector<uint8_t>&)>
AudioKeyCallback;
typedef std::function<void()> ConnectionEstabilishedCallback;
enum class RequestType : uint8_t {
@@ -82,7 +83,11 @@ class MercurySession : public bell::Task, public cspot::Session {
return this->executeSubscription(type, uri, callback, nullptr, parts);
}
void requestAudioKey(const std::vector<uint8_t>& trackId,
void unregister(uint64_t sequenceId);
void unregisterAudioKey(uint32_t sequenceId);
uint32_t requestAudioKey(const std::vector<uint8_t>& trackId,
const std::vector<uint8_t>& fileId,
AudioKeyCallback audioCallback);
@@ -108,7 +113,7 @@ class MercurySession : public bell::Task, public cspot::Session {
std::unordered_map<uint64_t, ResponseCallback> callbacks;
std::unordered_map<std::string, ResponseCallback> subscriptions;
AudioKeyCallback audioKeyCallback;
std::unordered_map<uint32_t, AudioKeyCallback> audioKeyCallbacks;
uint64_t sequenceId = 1;
uint32_t audioKeySequence = 1;

View File

@@ -6,7 +6,7 @@
#include "win32shim.h"
#else
#include <unistd.h> // for size_t
#include <unistd.h> // for size_t
#endif
#include <cstdint> // for uint8_t
#include <functional> // for function
@@ -37,8 +37,8 @@ class PlainConnection {
void readBlock(const uint8_t* dst, size_t size);
size_t writeBlock(const std::vector<uint8_t>& data);
private:
int apSock;
private:
int apSock;
};
} // namespace cspot

View File

@@ -1,10 +1,11 @@
#pragma once
#include <stdint.h> // for uint8_t, uint32_t
#include <memory> // for shared_ptr
#include <string> // for string
#include <vector> // for vector
#include <stdint.h> // for uint8_t, uint32_t
#include <memory> // for shared_ptr
#include <string> // for string
#include <vector> // for vector
#include "TrackReference.h"
#include "protobuf/spirc.pb.h" // for Frame, TrackRef, CapabilityType, Mess...
namespace cspot {
@@ -13,8 +14,10 @@ struct Context;
class PlaybackState {
private:
std::shared_ptr<cspot::Context> ctx;
uint32_t seqNum = 0;
uint8_t capabilityIndex = 0;
std::vector<uint8_t> frameData;
void addCapability(
@@ -24,6 +27,9 @@ class PlaybackState {
public:
Frame innerFrame;
Frame remoteFrame;
std::vector<TrackReference> remoteTracks;
enum class State { Playing, Stopped, Loading, Paused };
/**
@@ -74,56 +80,9 @@ class PlaybackState {
void setVolume(uint32_t volume);
/**
* @brief Enables queue shuffling.
*
* Sets shuffle parameter on local frame, and in case shuffling is enabled,
* it will randomize the entire local queue.
*
* @param shuffle whenever should shuffle
* @brief Updates local track queue from remote data.
*/
void setShuffle(bool shuffle);
/**
* @brief Enables repeat
*
* @param repeat should repeat param
*/
void setRepeat(bool repeat);
/**
* @brief Updates local track queue from remote data.
*/
void updateTracks();
/**
* @brief Changes playback to next queued track.
*
* Will go back to first track if current track is last track in queue.
* In that case, it will pause if repeat is disabled.
*/
bool nextTrack();
/**
* @brief Changes playback to previous queued track.
*
* Will stop if current track is the first track in queue and repeat is disabled.
* If repeat is enabled, it will loop back to the last track in queue.
*/
void prevTrack();
/**
* @brief Gets the current track reference.
*
* @return std::shared_ptr<TrackReference> pointer to track reference
*/
TrackRef* getCurrentTrackRef();
/**
* @brief Gets reference to next track in queue, or nullptr if there is no next track.
*
* @return std::shared_ptr<TrackReference> pointer to track reference
*/
TrackRef* getNextTrackRef();
void syncWithRemote();
/**
* @brief Encodes current frame into binary data via protobuf.
@@ -132,5 +91,7 @@ class PlaybackState {
* @return std::vector<uint8_t> binary frame data
*/
std::vector<uint8_t> encodeCurrentFrame(MessageType typ);
bool decodeRemoteFrame(std::vector<uint8_t>& data);
};
} // namespace cspot
} // namespace cspot

View File

@@ -4,41 +4,40 @@
#include <cstdint> // for uint32_t, uint8_t
#include <vector> // for vector
class Shannon
{
public:
static constexpr unsigned int N = 16;
class Shannon {
public:
static constexpr unsigned int N = 16;
void key(const std::vector<uint8_t> &key); /* set key */
void nonce(const std::vector<uint8_t> &nonce); /* set Init Vector */
void stream(std::vector<uint8_t> &buf); /* stream cipher */
void maconly(std::vector<uint8_t> &buf); /* accumulate MAC */
void encrypt(std::vector<uint8_t> &buf); /* encrypt + MAC */
void decrypt(std::vector<uint8_t> &buf); /* finalize + MAC */
void finish(std::vector<uint8_t> &buf); /* finalise MAC */
void key(const std::vector<uint8_t>& key); /* set key */
void nonce(const std::vector<uint8_t>& nonce); /* set Init Vector */
void stream(std::vector<uint8_t>& buf); /* stream cipher */
void maconly(std::vector<uint8_t>& buf); /* accumulate MAC */
void encrypt(std::vector<uint8_t>& buf); /* encrypt + MAC */
void decrypt(std::vector<uint8_t>& buf); /* finalize + MAC */
void finish(std::vector<uint8_t>& buf); /* finalise MAC */
private:
static constexpr unsigned int FOLD = Shannon::N;
static constexpr unsigned int INITKONST = 0x6996c53a;
static constexpr unsigned int KEYP = 13;
uint32_t R[Shannon::N];
uint32_t CRC[Shannon::N];
uint32_t initR[Shannon::N];
uint32_t konst;
uint32_t sbuf;
uint32_t mbuf;
int nbuf;
static uint32_t sbox1(uint32_t w);
static uint32_t sbox2(uint32_t w);
void cycle();
void crcfunc(uint32_t i);
void macfunc(uint32_t i);
void initState();
void saveState();
void reloadState();
void genkonst();
void diffuse();
void loadKey(const std::vector<uint8_t> &key);
private:
static constexpr unsigned int FOLD = Shannon::N;
static constexpr unsigned int INITKONST = 0x6996c53a;
static constexpr unsigned int KEYP = 13;
uint32_t R[Shannon::N];
uint32_t CRC[Shannon::N];
uint32_t initR[Shannon::N];
uint32_t konst;
uint32_t sbuf;
uint32_t mbuf;
int nbuf;
static uint32_t sbox1(uint32_t w);
static uint32_t sbox2(uint32_t w);
void cycle();
void crcfunc(uint32_t i);
void macfunc(uint32_t i);
void initState();
void saveState();
void reloadState();
void genkonst();
void diffuse();
void loadKey(const std::vector<uint8_t>& key);
};
#endif

View File

@@ -1,10 +1,10 @@
#ifndef SHANNONCONNECTION_H
#define SHANNONCONNECTION_H
#include <cstdint> // for uint8_t, uint32_t
#include <memory> // for shared_ptr, unique_ptr
#include <mutex> // for mutex
#include <vector> // for vector
#include <cstdint> // for uint8_t, uint32_t
#include <memory> // for shared_ptr, unique_ptr
#include <mutex> // for mutex
#include <vector> // for vector
#include "Packet.h" // for Packet

View File

@@ -1,14 +1,14 @@
#pragma once
#include <stdint.h> // for uint32_t, uint8_t
#include <functional> // for function
#include <memory> // for shared_ptr, unique_ptr
#include <string> // for string
#include <variant> // for variant
#include <vector> // for vector
#include <stdint.h> // for uint32_t, uint8_t
#include <functional> // for function
#include <memory> // for shared_ptr, unique_ptr
#include <string> // for string
#include <variant> // for variant
#include <vector> // for vector
#include "CDNTrackStream.h" // for CDNTrackStream, CDNTrackStream::Track...
#include "PlaybackState.h" // for PlaybackState
#include "CDNAudioFile.h" // for CDNTrackStream, CDNTrackStream::Track...
#include "TrackQueue.h"
#include "protobuf/spirc.pb.h" // for MessageType
namespace cspot {
@@ -31,7 +31,8 @@ class SpircHandler {
FLUSH,
PLAYBACK_START
};
typedef std::variant<CDNTrackStream::TrackInfo, int, bool> EventData;
typedef std::variant<TrackInfo, int, bool> EventData;
struct Event {
EventType eventType;
@@ -47,36 +48,34 @@ class SpircHandler {
void setPause(bool pause);
void nextSong();
void previousSong();
void nextSong();
void notifyAudioReachedPlayback();
void updatePositionMs(uint32_t position);
void setRemoteVolume(int volume);
void loadTrackFromURI(const std::string& uri);
std::shared_ptr<cspot::TrackQueue> getTrackQueue() { return trackQueue; }
void disconnect();
private:
std::shared_ptr<cspot::Context> ctx;
std::shared_ptr<cspot::TrackPlayer> trackPlayer;
std::shared_ptr<cspot::TrackQueue> trackQueue;
EventHandler eventHandler = nullptr;
cspot::PlaybackState playbackState;
CDNTrackStream::TrackInfo currentTrackInfo;
bool isTrackFresh = true;
bool isRequestedFromLoad = false;
bool isNextTrackPreloaded = false;
uint32_t nextTrackPosition = 0;
std::shared_ptr<cspot::PlaybackState> playbackState;
void sendCmd(MessageType typ);
void sendEvent(EventType type);
void sendEvent(EventType type, EventData data);
void skipSong(TrackQueue::SkipDirection dir);
void handleFrame(std::vector<uint8_t>& data);
void notify();
};
} // namespace cspot
} // namespace cspot

View File

@@ -1,47 +1,54 @@
#pragma once
#include <atomic> // for atomic
#include <cstdint> // for uint8_t, int64_t
#include <ctime> // for size_t, time
#include <functional> // for function
#include <memory> // for shared_ptr, unique_ptr
#include <mutex> // for mutex
#include <string_view> // for string_view
#include <vector> // for vector
#include <atomic> // for atomic
#include <cstdint> // for uint8_t, int64_t
#include <ctime> // for size_t, time
#include <functional> // for function
#include <memory> // for shared_ptr, unique_ptr
#include <mutex> // for mutex
#include <string_view> // for string_view
#include <vector> // for vector
#include "BellTask.h" // for Task
#include "CDNTrackStream.h" // for CDNTrackStream, CDNTrackStream::TrackInfo
#include "BellTask.h" // for Task
#include "CDNAudioFile.h"
#include "TrackQueue.h"
namespace bell {
class WrappedSemaphore;
} // namespace bell
#ifdef BELL_VORBIS_FLOAT
#include "vorbis/vorbisfile.h"
#else
#include "ivorbisfile.h" // for OggVorbis_File, ov_callbacks
#include "ivorbisfile.h" // for OggVorbis_File, ov_callbacks
#endif
namespace cspot {
class TrackProvider;
class TrackQueue;
struct Context;
struct TrackReference;
class TrackPlayer : bell::Task {
public:
typedef std::function<void()> TrackLoadedCallback;
typedef std::function<size_t(uint8_t*, size_t, std::string_view, size_t)> DataCallback;
// Callback types
typedef std::function<void(std::shared_ptr<QueuedTrack>)> TrackLoadedCallback;
typedef std::function<size_t(uint8_t*, size_t, std::string_view)> DataCallback;
typedef std::function<void()> EOFCallback;
typedef std::function<bool()> isAiringCallback;
TrackPlayer(std::shared_ptr<cspot::Context> ctx, isAiringCallback, EOFCallback, TrackLoadedCallback);
TrackPlayer(std::shared_ptr<cspot::Context> ctx,
std::shared_ptr<cspot::TrackQueue> trackQueue,
EOFCallback eofCallback, TrackLoadedCallback loadedCallback);
~TrackPlayer();
void loadTrackFromRef(TrackReference& ref, size_t playbackMs, bool startAutomatically);
void loadTrackFromRef(TrackReference& ref, size_t playbackMs,
bool startAutomatically);
void setDataCallback(DataCallback callback);
CDNTrackStream::TrackInfo getCurrentTrackInfo();
// CDNTrackStream::TrackInfo getCurrentTrackInfo();
void seekMs(size_t ms);
void stopTrack();
void resetState();
// Vorbis codec callbacks
size_t _vorbisRead(void* ptr, size_t size, size_t nmemb);
@@ -49,35 +56,39 @@ class TrackPlayer : bell::Task {
int _vorbisSeek(int64_t offset, int whence);
long _vorbisTell();
void destroy();
void stop();
void start();
private:
std::shared_ptr<cspot::Context> ctx;
std::shared_ptr<cspot::TrackProvider> trackProvider;
std::shared_ptr<cspot::CDNTrackStream> currentTrackStream;
size_t sequence = std::time(nullptr);
std::shared_ptr<cspot::TrackQueue> trackQueue;
std::shared_ptr<cspot::CDNAudioFile> currentTrackStream;
std::unique_ptr<bell::WrappedSemaphore> playbackSemaphore;
TrackLoadedCallback trackLoaded;
DataCallback dataCallback = nullptr;
EOFCallback eofCallback;
isAiringCallback isAiring;
// Playback control
std::atomic<bool> currentSongPlaying;
std::mutex playbackMutex;
std::mutex seekMutex;
std::mutex dataOutMutex;
// Vorbis related
OggVorbis_File vorbisFile;
ov_callbacks vorbisCallbacks;
int currentSection;
std::vector<uint8_t> pcmBuffer = std::vector<uint8_t>(1024);
size_t playbackPosition = 0;
bool autoStart = false;
std::atomic<bool> isRunning = true;
std::atomic<bool> isRunning = false;
std::atomic<bool> pendingReset = false;
std::atomic<bool> inFuture = false;
std::atomic<size_t> pendingSeekPositionMs = 0;
std::mutex runningMutex;
void runTask() override;

View File

@@ -1,51 +1,36 @@
#pragma once
#include <pb_encode.h>
#include <string_view>
#include <vector>
#include <optional>
#include "NanoPBHelper.h"
#include "Utils.h"
#include "pb_decode.h"
#include "protobuf/spirc.pb.h"
namespace cspot {
namespace cspot {
struct TrackReference {
static constexpr auto base62Alphabet =
"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
TrackReference();
// Resolved track GID
std::vector<uint8_t> gid;
std::string uri, context;
std::optional<bool> queued;
// Type identifier
enum class Type { TRACK, EPISODE };
Type type;
static TrackReference fromTrackRef(TrackRef* ref) {
TrackReference trackRef;
if (ref->gid != nullptr) {
// For tracks, the GID is already in the protobuf
trackRef.gid = pbArrayToVector(ref->gid);
trackRef.type = Type::TRACK;
} else {
// Episode GID is being fetched via base62 encoded URI
auto uri = std::string(ref->uri);
auto idString = uri.substr(uri.find_last_of(":") + 1, uri.size());
trackRef.gid = {0};
void decodeURI();
std::string_view alphabet(base62Alphabet);
for (int x = 0; x < idString.size(); x++) {
size_t d = alphabet.find(idString[x]);
trackRef.gid = bigNumMultiply(trackRef.gid, 62);
trackRef.gid = bigNumAdd(trackRef.gid, d);
}
}
bool operator==(const TrackReference& other) const;
return trackRef;
}
// Encodes list of track references into a pb structure, used by nanopb
static bool pbEncodeTrackList(pb_ostream_t* stream, const pb_field_t* field,
void* const* arg);
static TrackReference fromGID(std::vector<uint8_t> gid, bool isEpisode) {
TrackReference trackRef;
trackRef.gid = gid;
trackRef.type = isEpisode ? Type::EPISODE : Type::TRACK;
return trackRef;
}
static bool pbDecodeTrackList(pb_istream_t* stream, const pb_field_t* field,
void** arg);
};
} // namespace cspot
} // namespace cspot

View File

@@ -1,7 +1,7 @@
#ifndef UTILS_H
#define UTILS_H
#include <cstdio> // for snprintf, size_t
#include <vector> // for vector
#include <cstdio> // for snprintf, size_t
#include <vector> // for vector
#ifdef _WIN32
#include <winsock2.h>
#include <ws2tcpip.h>
@@ -55,7 +55,6 @@ std::vector<uint8_t> bigNumAdd(std::vector<uint8_t> num, int n);
unsigned char h2int(char c);
std::string urlDecode(std::string str);
/**
@@ -64,7 +63,7 @@ std::string urlDecode(std::string str);
* @param s string containing hex data
* @return std::vector<uint8_t> vector containing binary data
*/
std::vector<uint8_t> stringHexToBytes(const std::string &s);
std::vector<uint8_t> stringHexToBytes(const std::string& s);
/**
* @brief Converts provided bytes into a human readable hex string
@@ -72,7 +71,7 @@ std::vector<uint8_t> stringHexToBytes(const std::string &s);
* @param bytes vector containing binary data
* @return std::string string containing hex representation of inputted data
*/
std::string bytesToHexString(const std::vector<uint8_t> &bytes);
std::string bytesToHexString(const std::vector<uint8_t>& bytes);
/**
* @brief Extracts given type from binary data
@@ -83,8 +82,7 @@ std::string bytesToHexString(const std::vector<uint8_t> &bytes);
* @return T extracted type
*/
template <typename T>
T extract(const std::vector<unsigned char> &v, int pos)
{
T extract(const std::vector<unsigned char>& v, int pos) {
T value;
memcpy(&value, &v[pos], sizeof(T));
return value;
@@ -98,22 +96,25 @@ T extract(const std::vector<unsigned char> &v, int pos)
* @return std::vector<uint8_t> resulting vector containing binary data
*/
template <typename T>
std::vector<uint8_t> pack(T data)
{
std::vector<std::uint8_t> rawData( (std::uint8_t*)&data, (std::uint8_t*)&(data) + sizeof(T));
std::vector<uint8_t> pack(T data) {
std::vector<std::uint8_t> rawData((std::uint8_t*)&data,
(std::uint8_t*)&(data) + sizeof(T));
return rawData;
return rawData;
}
template<typename ... Args>
std::string string_format( const std::string& format, Args ... args )
{
int size_s = std::snprintf( nullptr, 0, format.c_str(), args ... ) + 1; // Extra space for '\0'
if( size_s <= 0 ){ throw std::runtime_error( "Error during formatting." ); }
auto size = static_cast<size_t>( size_s );
std::unique_ptr<char[]> buf( new char[ size ] );
std::snprintf( buf.get(), size, format.c_str(), args ... );
return std::string( buf.get(), buf.get() + size - 1 ); // We don't want the '\0' inside
template <typename... Args>
std::string string_format(const std::string& format, Args... args) {
int size_s = std::snprintf(nullptr, 0, format.c_str(), args...) +
1; // Extra space for '\0'
if (size_s <= 0) {
throw std::runtime_error("Error during formatting.");
}
auto size = static_cast<size_t>(size_s);
std::unique_ptr<char[]> buf(new char[size]);
std::snprintf(buf.get(), size, format.c_str(), args...);
return std::string(buf.get(),
buf.get() + size - 1); // We don't want the '\0' inside
}
#endif