mirror of
https://github.com/sle118/squeezelite-esp32.git
synced 2025-12-10 13:37:03 +03:00
more crap
This commit is contained in:
21
components/spotify/cspot/bell/src/BellSocket.cpp
Normal file
21
components/spotify/cspot/bell/src/BellSocket.cpp
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
// Copyright (c) Kuba Szczodrzyński 2021-12-21.
|
||||||
|
|
||||||
|
#include "BellSocket.h"
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
void bell::Socket::open(const std::string &url) {
|
||||||
|
auto *urlStr = url.c_str();
|
||||||
|
bool https = urlStr[4] == 's';
|
||||||
|
uint16_t port = https ? 443 : 80;
|
||||||
|
auto *hostname = urlStr + (https ? 8 : 7);
|
||||||
|
auto *hostnameEnd = strchr(hostname, ':');
|
||||||
|
auto *path = strchr(hostname, '/');
|
||||||
|
if (hostnameEnd == nullptr) {
|
||||||
|
hostnameEnd = path;
|
||||||
|
} else {
|
||||||
|
port = strtol(hostnameEnd + 1, nullptr, 10);
|
||||||
|
}
|
||||||
|
auto hostnameStr = std::string(hostname, (const char *)hostnameEnd);
|
||||||
|
|
||||||
|
this->open(hostnameStr, port);
|
||||||
|
}
|
||||||
273
components/spotify/cspot/bell/src/HTTPClient.cpp
Normal file
273
components/spotify/cspot/bell/src/HTTPClient.cpp
Normal file
@@ -0,0 +1,273 @@
|
|||||||
|
// Copyright (c) Kuba Szczodrzyński 2021-12-21.
|
||||||
|
|
||||||
|
#include "HTTPClient.h"
|
||||||
|
#include "TCPSocket.h"
|
||||||
|
|
||||||
|
using namespace bell;
|
||||||
|
|
||||||
|
struct HTTPClient::HTTPResponse *HTTPClient::execute(const struct HTTPRequest &request) {
|
||||||
|
auto *response = new HTTPResponse();
|
||||||
|
auto *url = request.url.c_str();
|
||||||
|
HTTPClient::executeImpl(request, url, response);
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
void HTTPClient::executeImpl(const struct HTTPRequest &request, const char *url, struct HTTPResponse *&response) {
|
||||||
|
bool https = url[4] == 's';
|
||||||
|
uint16_t port = https ? 443 : 80;
|
||||||
|
auto *hostname = url + (https ? 8 : 7);
|
||||||
|
auto *hostnameEnd = strchr(hostname, ':');
|
||||||
|
auto *path = strchr(hostname, '/');
|
||||||
|
if (hostnameEnd == nullptr) {
|
||||||
|
hostnameEnd = path;
|
||||||
|
} else {
|
||||||
|
port = strtol(hostnameEnd + 1, nullptr, 10);
|
||||||
|
}
|
||||||
|
auto hostnameStr = std::string(hostname, (const char *)hostnameEnd);
|
||||||
|
|
||||||
|
if (https) {
|
||||||
|
response->socket = std::make_shared<TLSSocket>();
|
||||||
|
} else {
|
||||||
|
response->socket = std::make_shared<TCPSocket>();
|
||||||
|
}
|
||||||
|
response->socket->open(hostnameStr, port);
|
||||||
|
|
||||||
|
const char *endl = "\r\n";
|
||||||
|
std::stringstream stream;
|
||||||
|
switch (request.method) {
|
||||||
|
case HTTPMethod::GET:
|
||||||
|
stream << "GET ";
|
||||||
|
break;
|
||||||
|
case HTTPMethod::POST:
|
||||||
|
stream << "POST ";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
stream << path << " HTTP/1.1" << endl;
|
||||||
|
stream << "Host: " << hostnameStr << ":" << port << endl;
|
||||||
|
stream << "Accept: */*" << endl;
|
||||||
|
if (!request.body.empty()) {
|
||||||
|
stream << "Content-Type: " << request.contentType << endl;
|
||||||
|
stream << "Content-Length: " << request.body.size() << endl;
|
||||||
|
}
|
||||||
|
for (const auto &header : request.headers) {
|
||||||
|
stream << header.first << ": " << header.second << endl;
|
||||||
|
}
|
||||||
|
stream << endl;
|
||||||
|
stream << request.body;
|
||||||
|
std::string data = stream.str();
|
||||||
|
|
||||||
|
size_t len = response->socket->write((uint8_t *)data.c_str(), data.size());
|
||||||
|
if (len != data.size()) {
|
||||||
|
response->close();
|
||||||
|
BELL_LOG(error, "http", "Writing failed: wrote %d of %d bytes", len, data.size());
|
||||||
|
free(response);
|
||||||
|
response = nullptr;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
response->readHeaders();
|
||||||
|
|
||||||
|
if (response->isRedirect && (request.maxRedirects < 0 || response->redirectCount < request.maxRedirects)) {
|
||||||
|
response->redirectCount++;
|
||||||
|
response->close(); // close the previous socket
|
||||||
|
HTTPClient::executeImpl(request, response->location.c_str(), response);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HTTPClient::readHeader(const char *&header, const char *name) {
|
||||||
|
size_t len = strlen(name);
|
||||||
|
if (strncasecmp(header, name, len) == 0) {
|
||||||
|
header += len;
|
||||||
|
while (*header == ' ')
|
||||||
|
header++;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t HTTPClient::HTTPResponse::readRaw(char *dst) {
|
||||||
|
size_t len = this->socket->read((uint8_t *)dst, BUF_SIZE);
|
||||||
|
// BELL_LOG(debug, "http", "Read %d bytes", len);
|
||||||
|
this->bodyRead += len; // after reading headers this gets overwritten
|
||||||
|
dst[len] = '\0';
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
void HTTPClient::HTTPResponse::readHeaders() {
|
||||||
|
size_t len;
|
||||||
|
char *line, *lineEnd;
|
||||||
|
bool complete = false;
|
||||||
|
std::string lineBuf;
|
||||||
|
|
||||||
|
if (this->buf == nullptr) { // allocate a buffer
|
||||||
|
this->buf = static_cast<char *>(malloc(BUF_SIZE + 1));
|
||||||
|
this->bufPtr = this->buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
// reset everything after a redirect
|
||||||
|
this->statusCode = 0;
|
||||||
|
this->contentLength = 0;
|
||||||
|
this->isChunked = false;
|
||||||
|
this->isGzip = false;
|
||||||
|
this->isComplete = false;
|
||||||
|
this->isRedirect = false;
|
||||||
|
this->isStreaming = false;
|
||||||
|
do {
|
||||||
|
len = this->readRaw(this->buf);
|
||||||
|
line = this->buf;
|
||||||
|
do {
|
||||||
|
lineEnd = strstr(line, "\r\n");
|
||||||
|
if (!lineEnd) {
|
||||||
|
lineBuf += std::string(line, this->buf + len);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
lineBuf += std::string(line, lineEnd);
|
||||||
|
if (lineBuf.empty()) {
|
||||||
|
complete = true;
|
||||||
|
// if body is present in buf, move the reading pointer
|
||||||
|
if (lineEnd + 2 < this->buf + len) {
|
||||||
|
this->bufPtr = lineEnd + 2;
|
||||||
|
this->bufRemaining = len - (this->bufPtr - this->buf);
|
||||||
|
this->bodyRead = this->bufRemaining;
|
||||||
|
this->isStreaming =
|
||||||
|
!this->isComplete && !this->contentLength && (len < BUF_SIZE || this->socket->poll() == 0);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto *header = lineBuf.c_str();
|
||||||
|
if (strncmp(header, "HTTP/", 5) == 0) {
|
||||||
|
header += 9; // skip "1.1 "
|
||||||
|
this->statusCode = strtol(header, nullptr, 10);
|
||||||
|
} else if (readHeader(header, "content-type:")) {
|
||||||
|
this->contentType = std::string(header);
|
||||||
|
} else if (readHeader(header, "content-length:")) {
|
||||||
|
this->contentLength = strtol(header, nullptr, 10);
|
||||||
|
if (!this->contentLength)
|
||||||
|
this->isComplete = true; // forbid reading of the body
|
||||||
|
} else if (readHeader(header, "transfer-encoding:")) {
|
||||||
|
this->isChunked = strncmp(header, "chunked", 7) == 0;
|
||||||
|
} else if (readHeader(header, "location:")) {
|
||||||
|
this->isRedirect = true;
|
||||||
|
this->location = std::string(header);
|
||||||
|
} else {
|
||||||
|
char *colonPtr = (char*) strchr(header, ':');
|
||||||
|
if (colonPtr) {
|
||||||
|
auto *valuePtr = colonPtr + 1;
|
||||||
|
while (*valuePtr == ' ')
|
||||||
|
valuePtr++;
|
||||||
|
*colonPtr = '\0';
|
||||||
|
for (auto *p = (char *)header; *p; ++p) // convert header name to lower case
|
||||||
|
*p = (char)tolower(*p);
|
||||||
|
this->headers[std::string(header)] = std::string(valuePtr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lineBuf.clear();
|
||||||
|
line = lineEnd + 2; // skip \r\n
|
||||||
|
} while (true);
|
||||||
|
} while (!complete);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HTTPClient::HTTPResponse::skip(size_t len, bool dontRead) {
|
||||||
|
size_t skip = 0;
|
||||||
|
if (len > bufRemaining) {
|
||||||
|
skip = len - bufRemaining;
|
||||||
|
len = bufRemaining;
|
||||||
|
}
|
||||||
|
bufRemaining -= len;
|
||||||
|
bufPtr += len;
|
||||||
|
if (!bufRemaining && !dontRead) { // don't read more data after a chunk's \r\n
|
||||||
|
if (isComplete || (contentLength && bodyRead >= contentLength && !chunkRemaining)) {
|
||||||
|
isComplete = true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
bufRemaining = this->readRaw(this->buf);
|
||||||
|
if (!bufRemaining)
|
||||||
|
return false; // no more data - shouldn't happen for valid responses
|
||||||
|
bufPtr = this->buf + skip;
|
||||||
|
bufRemaining -= skip;
|
||||||
|
if (!contentLength && bufRemaining < BUF_SIZE) {
|
||||||
|
// no content length set and the TCP buffer is not yielding more data, yet
|
||||||
|
isStreaming = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t HTTPClient::HTTPResponse::read(char *dst, size_t toRead) {
|
||||||
|
if (isComplete) {
|
||||||
|
// end of chunked stream was found OR complete body was read
|
||||||
|
dst[0] = '\0';
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
auto *dstStart = dst;
|
||||||
|
size_t read = 0;
|
||||||
|
while (toRead) { // this loop ends after original toRead
|
||||||
|
skip(0); // ensure the buffer contains data, wait if necessary
|
||||||
|
if (isChunked && !chunkRemaining) {
|
||||||
|
if (*bufPtr == '0') { // all chunks were read *and emitted*
|
||||||
|
isComplete = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
auto *endPtr = bufPtr;
|
||||||
|
if (strchr(bufPtr, '\r') == nullptr) { // buf doesn't contain complete chunk size
|
||||||
|
auto size = std::string(bufPtr, bufPtr + bufRemaining); // take the rest of the buffer
|
||||||
|
if (!skip(bufRemaining)) // skip the rest, read another buf
|
||||||
|
break; // -> no more data
|
||||||
|
endPtr = strchr(bufPtr, '\r'); // find the end of the actual number
|
||||||
|
if (endPtr == nullptr) // something's wrong
|
||||||
|
break; // - give up
|
||||||
|
size += std::string(bufPtr, endPtr); // append the newly read size
|
||||||
|
chunkRemaining = std::stoul(size, nullptr, 16); // read the hex size
|
||||||
|
} else {
|
||||||
|
chunkRemaining = strtol(bufPtr, &endPtr, 16); // read the hex size
|
||||||
|
}
|
||||||
|
if (!skip(endPtr - bufPtr + 2)) // skip the size and \r\n
|
||||||
|
break; // -> no more data, break out of main loop
|
||||||
|
} else if (contentLength && !chunkRemaining) {
|
||||||
|
chunkRemaining = contentLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (chunkRemaining && toRead) {
|
||||||
|
size_t count = std::min(toRead, std::min(bufRemaining, chunkRemaining));
|
||||||
|
strncpy(dst, bufPtr, count);
|
||||||
|
dst += count; // move the dst pointer
|
||||||
|
read += count; // increment read counter
|
||||||
|
chunkRemaining -= count; // decrease chunk remaining size
|
||||||
|
toRead -= count; // decrease local remaining size
|
||||||
|
if (!skip(count)) { // eat some buffer
|
||||||
|
toRead = 0; // -> no more data, break out of main loop
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (isChunked && !chunkRemaining && !skip(2, isStreaming)) // skip the \r\n for chunked encoding
|
||||||
|
toRead = 0; // -> no more data, break out of main loop
|
||||||
|
}
|
||||||
|
if (isStreaming && !bufRemaining) { // stream with no buffer available, just yield the current chunk
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!isChunked && contentLength && !chunkRemaining)
|
||||||
|
isComplete = true;
|
||||||
|
// BELL_LOG(debug, "http", "Read %d of %d bytes", bodyRead, contentLength);
|
||||||
|
dstStart[read] = '\0';
|
||||||
|
return read;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string HTTPClient::HTTPResponse::readToString() {
|
||||||
|
if (this->contentLength) {
|
||||||
|
std::string result(this->contentLength, '\0');
|
||||||
|
this->read(result.data(), this->contentLength);
|
||||||
|
this->close();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
std::string result;
|
||||||
|
char buffer[BUF_SIZE];
|
||||||
|
size_t len;
|
||||||
|
do {
|
||||||
|
len = this->read(buffer, BUF_SIZE);
|
||||||
|
result += std::string(buffer);
|
||||||
|
} while (len);
|
||||||
|
this->close();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
#ifndef PBCOMMON_H
|
|
||||||
#define PBCOMMON_H
|
|
||||||
|
|
||||||
#include <cstdint>
|
|
||||||
#include <PbWriter.h>
|
|
||||||
#include <optional>
|
|
||||||
#include <PbReader.h>
|
|
||||||
class BaseProtobufMessage
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
public:
|
|
||||||
bool firstField = true;
|
|
||||||
uint32_t lastMessagePosition;
|
|
||||||
void parseFromVector(std::vector<uint8_t> const &rawData)
|
|
||||||
{
|
|
||||||
auto reader = std::make_shared<PbReader>(rawData);
|
|
||||||
parseWithReader(reader);
|
|
||||||
}
|
|
||||||
void encodeToVector(std::vector<uint8_t> &rawData)
|
|
||||||
{
|
|
||||||
auto writer = std::make_shared<PbWriter>(rawData);
|
|
||||||
encodeWithWriter(writer);
|
|
||||||
}
|
|
||||||
|
|
||||||
void parseWithReader(std::shared_ptr<PbReader> reader)
|
|
||||||
{
|
|
||||||
firstField = true;
|
|
||||||
while (reader->next())
|
|
||||||
{
|
|
||||||
if (!decodeField(reader))
|
|
||||||
{
|
|
||||||
reader->skip();
|
|
||||||
} else {
|
|
||||||
firstField = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
virtual void encodeWithWriter(std::shared_ptr<PbWriter> writer) = 0;
|
|
||||||
virtual bool decodeField(std::shared_ptr<PbReader> reader) = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
#ifndef PBREADER_H
|
|
||||||
#define PBREADER_H
|
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
#include <string>
|
|
||||||
#include <memory>
|
|
||||||
#include <PbWireType.h>
|
|
||||||
|
|
||||||
class PbReader
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
std::vector<uint8_t> const &rawData;
|
|
||||||
uint32_t currentWireValue = 0;
|
|
||||||
uint64_t skipVarIntDump = 0;
|
|
||||||
uint32_t nextFieldLength = 0;
|
|
||||||
int64_t decodeZigzag(uint64_t value);
|
|
||||||
|
|
||||||
public:
|
|
||||||
PbReader(std::vector<uint8_t> const &rawData);
|
|
||||||
uint32_t maxPosition = 0;
|
|
||||||
|
|
||||||
PbWireType currentWireType = PbWireType::unknown;
|
|
||||||
uint32_t currentTag = 0;
|
|
||||||
uint32_t pos = 0;
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
T decodeVarInt();
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
T decodeFixed();
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
T decodeSVarInt();
|
|
||||||
void decodeString(std::string &target);
|
|
||||||
void decodeVector(std::vector<uint8_t> &target);
|
|
||||||
|
|
||||||
bool next();
|
|
||||||
void skip();
|
|
||||||
void resetMaxPosition();
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
#ifndef PBWIRETYPE_H
|
|
||||||
#define PBWIRETYPE_H
|
|
||||||
|
|
||||||
#include <cstdint>
|
|
||||||
|
|
||||||
enum class PbWireType : uint32_t
|
|
||||||
{
|
|
||||||
varint = 0, // int32/64, uint32/64, sint32/64, bool, enum
|
|
||||||
fixed64 = 1, // fixed64, sfixed64, double
|
|
||||||
length_delimited = 2, // string, bytes, nested messages, packed repeated fields
|
|
||||||
fixed32 = 5, // fixed32, sfixed32, float
|
|
||||||
unknown = 99
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
#ifndef PBWRITER_H
|
|
||||||
#define PBWRITER_H
|
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
#include <string>
|
|
||||||
#include <memory>
|
|
||||||
#include <PbWireType.h>
|
|
||||||
|
|
||||||
class PbWriter
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
std::vector<uint8_t> &rawData;
|
|
||||||
uint32_t pos;
|
|
||||||
uint32_t msgStartPos = 0;
|
|
||||||
void encodeVarInt(uint32_t low, uint32_t high, int32_t atIndex = 0);
|
|
||||||
uint32_t encodeZigzag32(int32_t value);
|
|
||||||
uint64_t encodeZigzag64(int64_t value);
|
|
||||||
public:
|
|
||||||
PbWriter(std::vector<uint8_t> &rawData);
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
void encodeVarInt(T, int32_t atIndex = 0);
|
|
||||||
template <typename T>
|
|
||||||
void encodeFixed(T);
|
|
||||||
void addSVarInt32(uint32_t tag, int32_t);
|
|
||||||
void addSVarInt64(uint32_t tag, int64_t);
|
|
||||||
void addString(uint32_t tag, std::string &target);
|
|
||||||
void addVector(uint32_t tag, std::vector<uint8_t> &target);
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
void addVarInt(uint32_t tag, T intType);
|
|
||||||
void addBool(uint32_t tag, bool value);
|
|
||||||
|
|
||||||
void addField(uint32_t tag, PbWireType wiretype);
|
|
||||||
uint32_t startMessage();
|
|
||||||
void finishMessage(uint32_t tag, uint32_t pos);
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
#ifndef PROTOBUF_H
|
|
||||||
#define PROTOBUF_H
|
|
||||||
#include <iostream>
|
|
||||||
#include <memory>
|
|
||||||
#include "protobuf.h"
|
|
||||||
#include <PbReader.h>
|
|
||||||
#include <PbCommon.h>
|
|
||||||
|
|
||||||
std::optional<AnyRef> findFieldWithProtobufTag(AnyRef ref, uint32_t tag);
|
|
||||||
void decodeField(std::shared_ptr<PbReader> reader, AnyRef any);
|
|
||||||
void decodeProtobuf(std::shared_ptr<PbReader> reader, AnyRef any);
|
|
||||||
void encodeProtobuf(std::shared_ptr<PbWriter> writer, AnyRef any, uint32_t protobufTag = 0);
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
std::vector<uint8_t> encodePb(T & data)
|
|
||||||
{
|
|
||||||
auto ref = AnyRef::of(&data);
|
|
||||||
std::vector<uint8_t> rawData;;
|
|
||||||
auto writer = std::make_shared<PbWriter>(rawData);
|
|
||||||
encodeProtobuf(writer, ref);
|
|
||||||
|
|
||||||
return rawData;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
T decodePb(std::vector<uint8_t> & bytes)
|
|
||||||
{
|
|
||||||
T data = {};
|
|
||||||
auto ref = AnyRef::of(&data);
|
|
||||||
auto writer = std::make_shared<PbReader>(bytes);
|
|
||||||
decodeProtobuf(writer, ref);
|
|
||||||
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,116 +0,0 @@
|
|||||||
#include "PbReader.h"
|
|
||||||
#include <iostream>
|
|
||||||
|
|
||||||
PbReader::PbReader(std::vector<uint8_t> const &rawData) : rawData(rawData)
|
|
||||||
{
|
|
||||||
maxPosition = rawData.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
T PbReader::decodeVarInt()
|
|
||||||
{
|
|
||||||
uint8_t byte;
|
|
||||||
uint_fast8_t bitpos = 0;
|
|
||||||
uint64_t storago = 0;
|
|
||||||
do
|
|
||||||
{
|
|
||||||
byte = this->rawData[pos];
|
|
||||||
pos++;
|
|
||||||
|
|
||||||
storago |= (uint64_t)(byte & 0x7F) << bitpos;
|
|
||||||
bitpos = (uint_fast8_t)(bitpos + 7);
|
|
||||||
} while (byte & 0x80);
|
|
||||||
return static_cast<T>(storago);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
T PbReader::decodeFixed()
|
|
||||||
{
|
|
||||||
pos += sizeof(T);
|
|
||||||
return *(T*)(&this->rawData[pos - sizeof(T)]);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
template int32_t PbReader::decodeFixed();
|
|
||||||
template int64_t PbReader::decodeFixed();
|
|
||||||
|
|
||||||
template uint32_t PbReader::decodeVarInt();
|
|
||||||
template int64_t PbReader::decodeVarInt();
|
|
||||||
template int32_t PbReader::decodeVarInt();
|
|
||||||
template bool PbReader::decodeVarInt();
|
|
||||||
|
|
||||||
void PbReader::resetMaxPosition()
|
|
||||||
{
|
|
||||||
maxPosition = rawData.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
void PbReader::decodeString(std::string &target)
|
|
||||||
{
|
|
||||||
nextFieldLength = decodeVarInt<uint32_t>();
|
|
||||||
target.resize(nextFieldLength);
|
|
||||||
// std::cout << "rawData.size() = " << rawData.size() << " pos = " << pos << " nextFieldLength =" << nextFieldLength;
|
|
||||||
// printf("\n%d, \n", currentTag);
|
|
||||||
// if (pos + nextFieldLength >= rawData.size())
|
|
||||||
// {
|
|
||||||
// std::cout << " \nBAD -- pos + nextFieldLength >= rawData.size() MSVC IS LITERLALLY SHAKING AND CRYING RN";
|
|
||||||
// }
|
|
||||||
// std::cout << std::endl;
|
|
||||||
std::copy(rawData.begin() + pos, rawData.begin() + pos + nextFieldLength, target.begin());
|
|
||||||
pos += nextFieldLength;
|
|
||||||
}
|
|
||||||
|
|
||||||
void PbReader::decodeVector(std::vector<uint8_t> &target)
|
|
||||||
{
|
|
||||||
nextFieldLength = decodeVarInt<uint32_t>();
|
|
||||||
target.resize(nextFieldLength);
|
|
||||||
std::copy(rawData.begin() + pos, rawData.begin() + pos + nextFieldLength, target.begin());
|
|
||||||
pos += nextFieldLength;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool PbReader::next()
|
|
||||||
{
|
|
||||||
if (pos >= maxPosition)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
currentWireValue = decodeVarInt<uint32_t>();
|
|
||||||
currentTag = currentWireValue >> 3U;
|
|
||||||
currentWireType = PbWireType(currentWireValue & 0x07U);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
int64_t PbReader::decodeZigzag(uint64_t value)
|
|
||||||
{
|
|
||||||
return static_cast<int64_t>((value >> 1U) ^ static_cast<uint64_t>(-static_cast<int64_t>(value & 1U)));
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
T PbReader::decodeSVarInt()
|
|
||||||
{
|
|
||||||
skipVarIntDump = decodeVarInt<uint64_t>();
|
|
||||||
return static_cast<T>(decodeZigzag(skipVarIntDump));
|
|
||||||
}
|
|
||||||
|
|
||||||
template int32_t PbReader::decodeSVarInt();
|
|
||||||
template int64_t PbReader::decodeSVarInt();
|
|
||||||
|
|
||||||
void PbReader::skip()
|
|
||||||
{
|
|
||||||
switch (currentWireType)
|
|
||||||
{
|
|
||||||
case PbWireType::varint:
|
|
||||||
skipVarIntDump = decodeVarInt<uint64_t>();
|
|
||||||
break;
|
|
||||||
case PbWireType::fixed64:
|
|
||||||
pos += 8;
|
|
||||||
break;
|
|
||||||
case PbWireType::length_delimited:
|
|
||||||
nextFieldLength = decodeVarInt<uint32_t>();
|
|
||||||
pos += nextFieldLength;
|
|
||||||
break;
|
|
||||||
case PbWireType::fixed32:
|
|
||||||
pos += 4;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,142 +0,0 @@
|
|||||||
#include "PbWriter.h"
|
|
||||||
|
|
||||||
PbWriter::PbWriter(std::vector<uint8_t> &rawData) : rawData(rawData)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void PbWriter::encodeVarInt(uint32_t low, uint32_t high, int32_t indexOffset)
|
|
||||||
{
|
|
||||||
size_t i = 0;
|
|
||||||
uint8_t byte = (uint8_t)(low & 0x7F);
|
|
||||||
low >>= 7;
|
|
||||||
|
|
||||||
while (i < 4 && (low != 0 || high != 0))
|
|
||||||
{
|
|
||||||
byte |= 0x80;
|
|
||||||
rawData.insert(rawData.end() + indexOffset, byte);
|
|
||||||
i++;
|
|
||||||
byte = (uint8_t)(low & 0x7F);
|
|
||||||
low >>= 7;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (high)
|
|
||||||
{
|
|
||||||
byte = (uint8_t)(byte | ((high & 0x07) << 4));
|
|
||||||
high >>= 3;
|
|
||||||
|
|
||||||
while (high)
|
|
||||||
{
|
|
||||||
byte |= 0x80;
|
|
||||||
rawData.insert(rawData.end() + indexOffset, byte);
|
|
||||||
i++;
|
|
||||||
byte = (uint8_t)(high & 0x7F);
|
|
||||||
high >>= 7;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
rawData.insert(rawData.end() + indexOffset, byte);
|
|
||||||
}
|
|
||||||
|
|
||||||
template void PbWriter::encodeVarInt(uint8_t, int32_t);
|
|
||||||
template void PbWriter::encodeVarInt(uint32_t, int32_t);
|
|
||||||
template void PbWriter::encodeVarInt(uint64_t, int32_t);
|
|
||||||
template void PbWriter::encodeVarInt(long long, int32_t);
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
void PbWriter::encodeVarInt(T data, int32_t offset)
|
|
||||||
{
|
|
||||||
auto value = static_cast<uint64_t>(data);
|
|
||||||
if (value <= 0x7F)
|
|
||||||
{
|
|
||||||
rawData.insert(rawData.end() + offset, (uint8_t)value);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
encodeVarInt((uint32_t)value, (uint32_t)(value >> 32), offset);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t PbWriter::encodeZigzag32(int32_t value) {
|
|
||||||
return (static_cast<uint32_t>(value) << 1U) ^ static_cast<uint32_t>(-static_cast<int32_t>(static_cast<uint32_t>(value) >> 31U));
|
|
||||||
}
|
|
||||||
|
|
||||||
uint64_t PbWriter::encodeZigzag64(int64_t value) {
|
|
||||||
return (static_cast<uint64_t>(value) << 1U) ^ static_cast<uint64_t>(-static_cast<int64_t>(static_cast<uint64_t>(value) >> 63U));
|
|
||||||
}
|
|
||||||
|
|
||||||
void PbWriter::addSVarInt32(uint32_t tag, int32_t data) {
|
|
||||||
auto val = encodeZigzag32(data);
|
|
||||||
addVarInt(tag, val);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
void PbWriter::encodeFixed(T data) {
|
|
||||||
auto val = reinterpret_cast<const char*>(&data);
|
|
||||||
rawData.insert(rawData.end(), val, val + sizeof(T));
|
|
||||||
}
|
|
||||||
|
|
||||||
template void PbWriter::encodeFixed(int64_t);
|
|
||||||
template void PbWriter::encodeFixed(int32_t);
|
|
||||||
|
|
||||||
void PbWriter::addSVarInt64(uint32_t tag, int64_t data) {
|
|
||||||
auto val = encodeZigzag64(data);
|
|
||||||
addVarInt(tag, val);
|
|
||||||
}
|
|
||||||
|
|
||||||
void PbWriter::addString(uint32_t tag, std::string &target)
|
|
||||||
{
|
|
||||||
addField(tag, PbWireType::length_delimited);
|
|
||||||
uint32_t stringSize = target.size();
|
|
||||||
encodeVarInt(stringSize);
|
|
||||||
|
|
||||||
rawData.insert(rawData.end(), target.begin(), target.end());
|
|
||||||
}
|
|
||||||
|
|
||||||
void PbWriter::addVector(uint32_t tag, std::vector<uint8_t> &target)
|
|
||||||
{
|
|
||||||
addField(tag, PbWireType::length_delimited);
|
|
||||||
uint32_t vectorSize = target.size();
|
|
||||||
encodeVarInt(vectorSize);
|
|
||||||
|
|
||||||
rawData.insert(rawData.end(), target.begin(), target.end());
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
void PbWriter::addVarInt(uint32_t tag, T intType)
|
|
||||||
{
|
|
||||||
addField(tag, PbWireType::varint);
|
|
||||||
encodeVarInt(intType);
|
|
||||||
}
|
|
||||||
|
|
||||||
void PbWriter::addBool(uint32_t tag, bool value)
|
|
||||||
{
|
|
||||||
addField(tag, PbWireType::varint);
|
|
||||||
rawData.push_back(char(value));
|
|
||||||
}
|
|
||||||
|
|
||||||
template void PbWriter::addVarInt(uint32_t, uint8_t);
|
|
||||||
template void PbWriter::addVarInt(uint32_t, uint32_t);
|
|
||||||
template void PbWriter::addVarInt(uint32_t, uint64_t);
|
|
||||||
template void PbWriter::addVarInt(uint32_t, int64_t);
|
|
||||||
template void PbWriter::addVarInt(uint32_t, bool);
|
|
||||||
|
|
||||||
void PbWriter::addField(uint32_t tag, PbWireType wiretype)
|
|
||||||
{
|
|
||||||
const uint32_t value = (tag << 3U) | uint32_t(wiretype);
|
|
||||||
encodeVarInt(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t PbWriter::startMessage()
|
|
||||||
{
|
|
||||||
return rawData.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
void PbWriter::finishMessage(uint32_t tag, uint32_t lastMessagePosition)
|
|
||||||
{
|
|
||||||
uint32_t finalMessageSize = rawData.size() - lastMessagePosition;
|
|
||||||
uint32_t msgHeader = (tag << 3U) | uint32_t(PbWireType::length_delimited);
|
|
||||||
|
|
||||||
int32_t offset = -finalMessageSize;
|
|
||||||
encodeVarInt(msgHeader, offset);
|
|
||||||
encodeVarInt(finalMessageSize, offset);
|
|
||||||
}
|
|
||||||
@@ -1,193 +0,0 @@
|
|||||||
#include "ProtoHelper.h"
|
|
||||||
|
|
||||||
|
|
||||||
std::optional<AnyRef> findFieldWithProtobufTag(AnyRef ref, uint32_t tag)
|
|
||||||
{
|
|
||||||
auto info = ref.reflectType();
|
|
||||||
for (int i = 0; i < info->fields.size(); i++)
|
|
||||||
{
|
|
||||||
|
|
||||||
if (tag == info->fields[i].protobufTag)
|
|
||||||
{
|
|
||||||
return std::make_optional(ref.getField(i));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
|
|
||||||
void decodeField(std::shared_ptr<PbReader> reader, AnyRef any)
|
|
||||||
{
|
|
||||||
auto fieldInfo = any.reflectType();
|
|
||||||
|
|
||||||
if (fieldInfo->kind == ReflectTypeKind::Optional)
|
|
||||||
{
|
|
||||||
auto optionalRef = AnyOptionalRef(any);
|
|
||||||
optionalRef.emplaceEmpty();
|
|
||||||
return decodeField(reader, optionalRef.get());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fieldInfo->kind == ReflectTypeKind::Class)
|
|
||||||
{
|
|
||||||
// Handle submessage
|
|
||||||
auto lastMaxPosition = reader->maxPosition;
|
|
||||||
auto messageSize = reader->decodeVarInt<uint32_t>();
|
|
||||||
|
|
||||||
reader->maxPosition = messageSize + reader->pos;
|
|
||||||
while (reader->next())
|
|
||||||
{
|
|
||||||
auto res = findFieldWithProtobufTag(any, reader->currentTag);
|
|
||||||
if (res.has_value())
|
|
||||||
{
|
|
||||||
decodeField(reader, res.value());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
reader->skip();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
reader->maxPosition = lastMaxPosition;
|
|
||||||
return;
|
|
||||||
} else if (any.is<std::vector<uint8_t>>())
|
|
||||||
{
|
|
||||||
reader->decodeVector(*any.as<std::vector<uint8_t>>());
|
|
||||||
}
|
|
||||||
// Handle repeated
|
|
||||||
else if (fieldInfo->kind == ReflectTypeKind::Vector)
|
|
||||||
{
|
|
||||||
auto aVec = AnyVectorRef(any);
|
|
||||||
aVec.emplace_back();
|
|
||||||
auto value = aVec.at(aVec.size() - 1);
|
|
||||||
auto valInfo = value.reflectType();
|
|
||||||
|
|
||||||
// Represents packed repeated encoding
|
|
||||||
if (valInfo->kind == ReflectTypeKind::Primitive && !value.is<std::string>() && !value.is<std::vector<uint8_t>>())
|
|
||||||
{
|
|
||||||
// *any.as<int64_t>() = reader->decodeVarInt<int64_t>();
|
|
||||||
reader->skip();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
decodeField(reader, value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (fieldInfo->kind == ReflectTypeKind::Enum)
|
|
||||||
{
|
|
||||||
*any.as<uint32_t>() = reader->decodeVarInt<uint32_t>();
|
|
||||||
}
|
|
||||||
else if (any.is<std::string>())
|
|
||||||
{
|
|
||||||
reader->decodeString(*any.as<std::string>());
|
|
||||||
}
|
|
||||||
else if (any.is<bool>())
|
|
||||||
{
|
|
||||||
*any.as<bool>() = reader->decodeVarInt<bool>();
|
|
||||||
}
|
|
||||||
else if (any.is<uint32_t>())
|
|
||||||
{
|
|
||||||
*any.as<uint32_t>() = reader->decodeVarInt<uint32_t>();
|
|
||||||
}
|
|
||||||
else if (any.is<int64_t>())
|
|
||||||
{
|
|
||||||
*any.as<int64_t>() = reader->decodeVarInt<int64_t>();
|
|
||||||
}
|
|
||||||
else if (any.is<int32_t>())
|
|
||||||
{
|
|
||||||
*any.as<int32_t>() = reader->decodeVarInt<int32_t>();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
reader->skip();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void decodeProtobuf(std::shared_ptr<PbReader> reader, AnyRef any)
|
|
||||||
{
|
|
||||||
while (reader->next())
|
|
||||||
{
|
|
||||||
auto res = findFieldWithProtobufTag(any, reader->currentTag);
|
|
||||||
if (res.has_value())
|
|
||||||
{
|
|
||||||
decodeField(reader, res.value());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
reader->skip();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void encodeProtobuf(std::shared_ptr<PbWriter> writer, AnyRef any, uint32_t protobufTag)
|
|
||||||
{
|
|
||||||
auto info = any.reflectType();
|
|
||||||
|
|
||||||
// Handle optionals, only encode if have value
|
|
||||||
if (info->kind == ReflectTypeKind::Optional)
|
|
||||||
{
|
|
||||||
auto optionalRef = AnyOptionalRef(any);
|
|
||||||
if (!optionalRef.has_value())
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return encodeProtobuf(writer, optionalRef.get(), protobufTag);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (info->kind == ReflectTypeKind::Class)
|
|
||||||
{
|
|
||||||
uint32_t startMsgPosition;
|
|
||||||
// 0 - default value, indicating top level message
|
|
||||||
if (protobufTag > 0)
|
|
||||||
{
|
|
||||||
startMsgPosition = writer->startMessage();
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < info->fields.size(); i++)
|
|
||||||
{
|
|
||||||
auto field = any.getField(i);
|
|
||||||
encodeProtobuf(writer, field, info->fields[i].protobufTag);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (protobufTag > 0)
|
|
||||||
{
|
|
||||||
writer->finishMessage(protobufTag, startMsgPosition);
|
|
||||||
}
|
|
||||||
} else if (any.is<std::vector<uint8_t>>())
|
|
||||||
{
|
|
||||||
writer->addVector(protobufTag, *any.as<std::vector<uint8_t>>());
|
|
||||||
}
|
|
||||||
else if (info->kind == ReflectTypeKind::Vector) {
|
|
||||||
auto aVec = AnyVectorRef(any);
|
|
||||||
auto size = aVec.size();
|
|
||||||
for (size_t i = 0; i < size; i++)
|
|
||||||
{
|
|
||||||
auto valueAt = aVec.at(i);
|
|
||||||
encodeProtobuf(writer, valueAt, protobufTag);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (info->kind == ReflectTypeKind::Enum) {
|
|
||||||
writer->addVarInt<uint32_t>(protobufTag, *any.as<uint32_t>());
|
|
||||||
}
|
|
||||||
else if (info->kind == ReflectTypeKind::Primitive)
|
|
||||||
{
|
|
||||||
if (any.is<std::string>())
|
|
||||||
{
|
|
||||||
writer->addString(protobufTag, *any.as<std::string>());
|
|
||||||
}
|
|
||||||
else if (any.is<uint32_t>())
|
|
||||||
{
|
|
||||||
writer->addVarInt<uint32_t>(protobufTag, *any.as<uint32_t>());
|
|
||||||
}
|
|
||||||
else if (any.is<uint64_t>())
|
|
||||||
{
|
|
||||||
writer->addVarInt<uint64_t>(protobufTag, *any.as<uint64_t>());
|
|
||||||
}
|
|
||||||
else if (any.is<int64_t>()) {
|
|
||||||
writer->addVarInt<int64_t>(protobufTag, *any.as<int64_t>());
|
|
||||||
} else if (any.is<bool>())
|
|
||||||
{
|
|
||||||
writer->addVarInt<bool>(protobufTag, *any.as<bool>());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user