unsigned char変数にバイナリデータがあります。cでそれらをPEM base64に変換する必要があります。opensslライブラリを調べましたが、関数が見つかりませんでした。体は何か考えを持っていますか?
unsigned char変数にバイナリデータがあります。cでそれらをPEM base64に変換する必要があります。opensslライブラリを調べましたが、関数が見つかりませんでした。体は何か考えを持っていますか?
回答:
これが私が使っているものです:
#include <stdint.h>
#include <stdlib.h>
static char encoding_table[] = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
'w', 'x', 'y', 'z', '0', '1', '2', '3',
'4', '5', '6', '7', '8', '9', '+', '/'};
static char *decoding_table = NULL;
static int mod_table[] = {0, 2, 1};
char *base64_encode(const unsigned char *data,
size_t input_length,
size_t *output_length) {
*output_length = 4 * ((input_length + 2) / 3);
char *encoded_data = malloc(*output_length);
if (encoded_data == NULL) return NULL;
for (int i = 0, j = 0; i < input_length;) {
uint32_t octet_a = i < input_length ? (unsigned char)data[i++] : 0;
uint32_t octet_b = i < input_length ? (unsigned char)data[i++] : 0;
uint32_t octet_c = i < input_length ? (unsigned char)data[i++] : 0;
uint32_t triple = (octet_a << 0x10) + (octet_b << 0x08) + octet_c;
encoded_data[j++] = encoding_table[(triple >> 3 * 6) & 0x3F];
encoded_data[j++] = encoding_table[(triple >> 2 * 6) & 0x3F];
encoded_data[j++] = encoding_table[(triple >> 1 * 6) & 0x3F];
encoded_data[j++] = encoding_table[(triple >> 0 * 6) & 0x3F];
}
for (int i = 0; i < mod_table[input_length % 3]; i++)
encoded_data[*output_length - 1 - i] = '=';
return encoded_data;
}
unsigned char *base64_decode(const char *data,
size_t input_length,
size_t *output_length) {
if (decoding_table == NULL) build_decoding_table();
if (input_length % 4 != 0) return NULL;
*output_length = input_length / 4 * 3;
if (data[input_length - 1] == '=') (*output_length)--;
if (data[input_length - 2] == '=') (*output_length)--;
unsigned char *decoded_data = malloc(*output_length);
if (decoded_data == NULL) return NULL;
for (int i = 0, j = 0; i < input_length;) {
uint32_t sextet_a = data[i] == '=' ? 0 & i++ : decoding_table[data[i++]];
uint32_t sextet_b = data[i] == '=' ? 0 & i++ : decoding_table[data[i++]];
uint32_t sextet_c = data[i] == '=' ? 0 & i++ : decoding_table[data[i++]];
uint32_t sextet_d = data[i] == '=' ? 0 & i++ : decoding_table[data[i++]];
uint32_t triple = (sextet_a << 3 * 6)
+ (sextet_b << 2 * 6)
+ (sextet_c << 1 * 6)
+ (sextet_d << 0 * 6);
if (j < *output_length) decoded_data[j++] = (triple >> 2 * 8) & 0xFF;
if (j < *output_length) decoded_data[j++] = (triple >> 1 * 8) & 0xFF;
if (j < *output_length) decoded_data[j++] = (triple >> 0 * 8) & 0xFF;
}
return decoded_data;
}
void build_decoding_table() {
decoding_table = malloc(256);
for (int i = 0; i < 64; i++)
decoding_table[(unsigned char) encoding_table[i]] = i;
}
void base64_cleanup() {
free(decoding_table);
}
これは、デコード時にエラーチェックを行わないことに注意してください。Base64でエンコードされていないデータは処理されます。
*output_length = ((input_length - 1) / 3) * 4 + 4;
base64_encodeの先頭で使用することにより、libmとmath.hの「依存関係」、および浮動小数点演算(一部のハードウェアでは遅い)の必要性をスキップでき ます。
build_decoding_table
ます。encoding_table[64]
するためにencoding_table[255]
存在していません。
私はこの質問がかなり古いことを知っていますが、提供されたソリューションの量に戸惑っていました-それぞれがより速くより良いと主張しています。base64エンコーダーとデコーダーを比較するプロジェクトをgithubにまとめました:https : //github.com/gaspardpetit/base64/
この時点では、私はCアルゴリズムに限定していません。1つの実装がC ++で適切に動作する場合、簡単にCにバックポートできます。また、テストはVisual Studio 2015を使用して行われました。誰かがclang /の結果でこの回答を更新したい場合gcc、私のゲストになります。
最速のエンコーダー:私が見つけた2つの最速のエンコーダー実装は、http: //web.mit.edu/freebsd/head/contrib/wpa/src/utils/base64.cのJouni Malinen とhttps://opensource.appleのApacheでした。 .com / source / QuickTimeStreamingServer / QuickTimeStreamingServer-452 / CommonUtilitiesLib / base64.c。
以下は、これまでにテストしたさまざまなアルゴリズムを使用して32Kのデータをエンコードする時間(マイクロ秒)です。
jounimalinen 25.1544
apache 25.5309
NibbleAndAHalf 38.4165
internetsoftwareconsortium 48.2879
polfosol 48.7955
wikibooks_org_c 51.9659
gnome 74.8188
elegantdice 118.899
libb64 120.601
manuelmartinez 120.801
arduino 126.262
daedalusalpha 126.473
CppCodec 151.866
wikibooks_org_cpp 343.2
adp_gmbh 381.523
LihO 406.693
libcurl 3246.39
user152949 4828.21
(RenéNyffeneggerのソリューションは、この質問に対する別の回答でクレジットされていますが、ここではadp_gmbhとしてリストされています)。
以下は、std :: stringを返すように少し変更したJouni Malinenからのものです。
/*
* Base64 encoding/decoding (RFC1341)
* Copyright (c) 2005-2011, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
// 2016-12-12 - Gaspard Petit : Slightly modified to return a std::string
// instead of a buffer allocated with malloc.
#include <string>
static const unsigned char base64_table[65] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
/**
* base64_encode - Base64 encode
* @src: Data to be encoded
* @len: Length of the data to be encoded
* @out_len: Pointer to output length variable, or %NULL if not used
* Returns: Allocated buffer of out_len bytes of encoded data,
* or empty string on failure
*/
std::string base64_encode(const unsigned char *src, size_t len)
{
unsigned char *out, *pos;
const unsigned char *end, *in;
size_t olen;
olen = 4*((len + 2) / 3); /* 3-byte blocks to 4-byte */
if (olen < len)
return std::string(); /* integer overflow */
std::string outStr;
outStr.resize(olen);
out = (unsigned char*)&outStr[0];
end = src + len;
in = src;
pos = out;
while (end - in >= 3) {
*pos++ = base64_table[in[0] >> 2];
*pos++ = base64_table[((in[0] & 0x03) << 4) | (in[1] >> 4)];
*pos++ = base64_table[((in[1] & 0x0f) << 2) | (in[2] >> 6)];
*pos++ = base64_table[in[2] & 0x3f];
in += 3;
}
if (end - in) {
*pos++ = base64_table[in[0] >> 2];
if (end - in == 1) {
*pos++ = base64_table[(in[0] & 0x03) << 4];
*pos++ = '=';
}
else {
*pos++ = base64_table[((in[0] & 0x03) << 4) |
(in[1] >> 4)];
*pos++ = base64_table[(in[1] & 0x0f) << 2];
}
*pos++ = '=';
}
return outStr;
}
最速のデコーダ:デコード結果は次のとおりです。少し驚いたことを認めなければなりません。
polfosol 45.2335
wikibooks_org_c 74.7347
apache 77.1438
libb64 100.332
gnome 114.511
manuelmartinez 126.579
elegantdice 138.514
daedalusalpha 151.561
jounimalinen 206.163
arduino 335.95
wikibooks_org_cpp 350.437
CppCodec 526.187
internetsoftwareconsortium 862.833
libcurl 1280.27
LihO 1852.4
adp_gmbh 1934.43
user152949 5332.87
C ++のbase64デコードスニペットからのPolfosolのスニペットは、ほぼ2倍の速さで最速です。
以下に、完全を期すためのコードを示します。
static const int B64index[256] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 62, 63, 62, 62, 63, 52, 53, 54, 55,
56, 57, 58, 59, 60, 61, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6,
7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0,
0, 0, 0, 63, 0, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51 };
std::string b64decode(const void* data, const size_t len)
{
unsigned char* p = (unsigned char*)data;
int pad = len > 0 && (len % 4 || p[len - 1] == '=');
const size_t L = ((len + 3) / 4 - pad) * 4;
std::string str(L / 4 * 3 + pad, '\0');
for (size_t i = 0, j = 0; i < L; i += 4)
{
int n = B64index[p[i]] << 18 | B64index[p[i + 1]] << 12 | B64index[p[i + 2]] << 6 | B64index[p[i + 3]];
str[j++] = n >> 16;
str[j++] = n >> 8 & 0xFF;
str[j++] = n & 0xFF;
}
if (pad)
{
int n = B64index[p[L]] << 18 | B64index[p[L + 1]] << 12;
str[str.size() - 1] = n >> 16;
if (len > L + 2 && p[L + 2] != '=')
{
n |= B64index[p[L + 2]] << 6;
str.push_back(n >> 8 & 0xFF);
}
}
return str;
}
char* outStr
パラメーターを追加し、std::string
必要に応じてaを返す代わりにそのバッファーに書き込みます。これは簡単です。これを投稿する前に、すでに2つのC ++回答と賛成投票がありました。
しかし、opensslでそれを行うこともできます(openssl enc
コマンドがそれを行う...)。BIO_f_base64()
関数を見てください。
これがOpenSSLを使用した私のソリューションです。
/* A BASE-64 ENCODER AND DECODER USING OPENSSL */
#include <openssl/pem.h>
#include <string.h> //Only needed for strlen().
char *base64encode (const void *b64_encode_this, int encode_this_many_bytes){
BIO *b64_bio, *mem_bio; //Declares two OpenSSL BIOs: a base64 filter and a memory BIO.
BUF_MEM *mem_bio_mem_ptr; //Pointer to a "memory BIO" structure holding our base64 data.
b64_bio = BIO_new(BIO_f_base64()); //Initialize our base64 filter BIO.
mem_bio = BIO_new(BIO_s_mem()); //Initialize our memory sink BIO.
BIO_push(b64_bio, mem_bio); //Link the BIOs by creating a filter-sink BIO chain.
BIO_set_flags(b64_bio, BIO_FLAGS_BASE64_NO_NL); //No newlines every 64 characters or less.
BIO_write(b64_bio, b64_encode_this, encode_this_many_bytes); //Records base64 encoded data.
BIO_flush(b64_bio); //Flush data. Necessary for b64 encoding, because of pad characters.
BIO_get_mem_ptr(mem_bio, &mem_bio_mem_ptr); //Store address of mem_bio's memory structure.
BIO_set_close(mem_bio, BIO_NOCLOSE); //Permit access to mem_ptr after BIOs are destroyed.
BIO_free_all(b64_bio); //Destroys all BIOs in chain, starting with b64 (i.e. the 1st one).
BUF_MEM_grow(mem_bio_mem_ptr, (*mem_bio_mem_ptr).length + 1); //Makes space for end null.
(*mem_bio_mem_ptr).data[(*mem_bio_mem_ptr).length] = '\0'; //Adds null-terminator to tail.
return (*mem_bio_mem_ptr).data; //Returns base-64 encoded data. (See: "buf_mem_st" struct).
}
char *base64decode (const void *b64_decode_this, int decode_this_many_bytes){
BIO *b64_bio, *mem_bio; //Declares two OpenSSL BIOs: a base64 filter and a memory BIO.
char *base64_decoded = calloc( (decode_this_many_bytes*3)/4+1, sizeof(char) ); //+1 = null.
b64_bio = BIO_new(BIO_f_base64()); //Initialize our base64 filter BIO.
mem_bio = BIO_new(BIO_s_mem()); //Initialize our memory source BIO.
BIO_write(mem_bio, b64_decode_this, decode_this_many_bytes); //Base64 data saved in source.
BIO_push(b64_bio, mem_bio); //Link the BIOs by creating a filter-source BIO chain.
BIO_set_flags(b64_bio, BIO_FLAGS_BASE64_NO_NL); //Don't require trailing newlines.
int decoded_byte_index = 0; //Index where the next base64_decoded byte should be written.
while ( 0 < BIO_read(b64_bio, base64_decoded+decoded_byte_index, 1) ){ //Read byte-by-byte.
decoded_byte_index++; //Increment the index until read of BIO decoded data is complete.
} //Once we're done reading decoded data, BIO_read returns -1 even though there's no error.
BIO_free_all(b64_bio); //Destroys all BIOs in chain, starting with b64 (i.e. the 1st one).
return base64_decoded; //Returns base-64 decoded data with trailing null terminator.
}
/*Here's one way to base64 encode/decode using the base64encode() and base64decode functions.*/
int main(void){
char data_to_encode[] = "Base64 encode this string!"; //The string we will base-64 encode.
int bytes_to_encode = strlen(data_to_encode); //Number of bytes in string to base64 encode.
char *base64_encoded = base64encode(data_to_encode, bytes_to_encode); //Base-64 encoding.
int bytes_to_decode = strlen(base64_encoded); //Number of bytes in string to base64 decode.
char *base64_decoded = base64decode(base64_encoded, bytes_to_decode); //Base-64 decoding.
printf("Original character string is: %s\n", data_to_encode); //Prints our initial string.
printf("Base-64 encoded string is: %s\n", base64_encoded); //Prints base64 encoded string.
printf("Base-64 decoded string is: %s\n", base64_decoded); //Prints base64 decoded string.
free(base64_encoded); //Frees up the memory holding our base64 encoded data.
free(base64_decoded); //Frees up the memory holding our base64 decoded data.
}
cc -o base base.c -lssl -lcrypto
。エラーはありません。これは、この出力を生成: Original character string is: Base64 encode this string! Base-64 encoded string is: QmFzZTY0IGVuY29kZSB0aGlzIHN0cmluZyE= Base-64 decoded string is: Base64 encode this string!
glibにはbase64エンコード用の関数があります:https : //developer.gnome.org/glib/stable/glib-Base64-Encoding.html
libb64には、CおよびC ++ APIの両方があります。それは軽量で、おそらく最も高速に公開されている実装です。これは専用のスタンドアロンbase64エンコーディングライブラリでもあり、OpenSSLやglibなどのより大きなライブラリを使用することで得られる他のすべてのものが必要ない場合に便利です。
#define BUFFERSIZE 16777216
より小さなバッファーが必要な場合は、65536に置き換えることができます。
char
ターゲットシステムで署名されていると想定していることがわかりました... base64_decode_value
負の数を返し、charにキャストされる可能性があるため、これは問題です。
GNU coreutilsはlib / base64にそれを持っています。少し肥大化していますが、EBCDICなどを扱っています。自分で遊ぶこともできます。
char base64_digit (n) unsigned n; {
if (n < 10) return n - '0';
else if (n < 10 + 26) return n - 'a';
else if (n < 10 + 26 + 26) return n - 'A';
else assert(0);
return 0;
}
unsigned char base64_decode_digit(char c) {
switch (c) {
case '=' : return 62;
case '.' : return 63;
default :
if (isdigit(c)) return c - '0';
else if (islower(c)) return c - 'a' + 10;
else if (isupper(c)) return c - 'A' + 10 + 26;
else assert(0);
}
return 0xff;
}
unsigned base64_decode(char *s) {
char *p;
unsigned n = 0;
for (p = s; *p; p++)
n = 64 * n + base64_decode_digit(*p);
return n;
}
これらのプレゼントによって、「自分で遊ぶ」と「標準の実装」を混同しないように、すべての人を知ってください。イェッシュ。
'+'
62と'/'
63です。以下は、base64エンコーディングバリアントのリストです。使用する文字の順序が異なるbase64エンコーディングバリアントが表示されません。しかし、アルゴリズムの背後にある数学は正しいです。
std :: stringで動作するC ++実装が必要でした。答えはどれも私のニーズを満たしていませんでした。エンコードとデコードのための単純な2関数ソリューションが必要でしたが、自分のコードを書くのが面倒だったので、これを見つけました。
http://www.adp-gmbh.ch/cpp/common/base64.html
コードのクレジットはRenéNyffeneggerに送られます。
サイトがダウンした場合に備えて、以下のコードを配置します。
base64.cpp
/*
base64.cpp and base64.h
Copyright (C) 2004-2008 René Nyffenegger
This source code is provided 'as-is', without any express or implied
warranty. In no event will the author be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this source code must not be misrepresented; you must not
claim that you wrote the original source code. If you use this source code
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original source code.
3. This notice may not be removed or altered from any source distribution.
René Nyffenegger rene.nyffenegger@adp-gmbh.ch
*/
#include "base64.h"
#include <iostream>
static const std::string base64_chars =
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789+/";
static inline bool is_base64(unsigned char c) {
return (isalnum(c) || (c == '+') || (c == '/'));
}
std::string base64_encode(unsigned char const* bytes_to_encode, unsigned int in_len) {
std::string ret;
int i = 0;
int j = 0;
unsigned char char_array_3[3];
unsigned char char_array_4[4];
while (in_len--) {
char_array_3[i++] = *(bytes_to_encode++);
if (i == 3) {
char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
char_array_4[3] = char_array_3[2] & 0x3f;
for(i = 0; (i <4) ; i++)
ret += base64_chars[char_array_4[i]];
i = 0;
}
}
if (i)
{
for(j = i; j < 3; j++)
char_array_3[j] = '\0';
char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
char_array_4[3] = char_array_3[2] & 0x3f;
for (j = 0; (j < i + 1); j++)
ret += base64_chars[char_array_4[j]];
while((i++ < 3))
ret += '=';
}
return ret;
}
std::string base64_decode(std::string const& encoded_string) {
int in_len = encoded_string.size();
int i = 0;
int j = 0;
int in_ = 0;
unsigned char char_array_4[4], char_array_3[3];
std::string ret;
while (in_len-- && ( encoded_string[in_] != '=') && is_base64(encoded_string[in_])) {
char_array_4[i++] = encoded_string[in_]; in_++;
if (i ==4) {
for (i = 0; i <4; i++)
char_array_4[i] = base64_chars.find(char_array_4[i]);
char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
for (i = 0; (i < 3); i++)
ret += char_array_3[i];
i = 0;
}
}
if (i) {
for (j = i; j <4; j++)
char_array_4[j] = 0;
for (j = 0; j <4; j++)
char_array_4[j] = base64_chars.find(char_array_4[j]);
char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
for (j = 0; (j < i - 1); j++) ret += char_array_3[j];
}
return ret;
}
base64.h
#include <string>
std::string base64_encode(unsigned char const* , unsigned int len);
std::string base64_decode(std::string const& s);
使用法
const std::string s = "test";
std::string encoded = base64_encode(reinterpret_cast<const unsigned char*>(s.c_str()), s.length());
std::string decoded = base64_decode(encoded);
これが私が長年使ってきたデコーダです...
static const char table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
static const int BASE64_INPUT_SIZE = 57;
BOOL isbase64(char c)
{
return c && strchr(table, c) != NULL;
}
inline char value(char c)
{
const char *p = strchr(table, c);
if(p) {
return p-table;
} else {
return 0;
}
}
int UnBase64(unsigned char *dest, const unsigned char *src, int srclen)
{
*dest = 0;
if(*src == 0)
{
return 0;
}
unsigned char *p = dest;
do
{
char a = value(src[0]);
char b = value(src[1]);
char c = value(src[2]);
char d = value(src[3]);
*p++ = (a << 2) | (b >> 4);
*p++ = (b << 4) | (c >> 2);
*p++ = (c << 6) | d;
if(!isbase64(src[1]))
{
p -= 2;
break;
}
else if(!isbase64(src[2]))
{
p -= 2;
break;
}
else if(!isbase64(src[3]))
{
p--;
break;
}
src += 4;
while(*src && (*src == 13 || *src == 10)) src++;
}
while(srclen-= 4);
*p = 0;
return p-dest;
}
UnBase64
そのバッファがbase 64エンコードされた文字列をデコードするために必要な正確なサイズである場合、関数はdestバッファの後のメモリを危険にさらす可能性があります。たとえば、次のbase 64エンコードされた文字列「BQ ==」を単一のBYTEにデコードしようとする単純なケースを考えてください。つまりunsigned char Result = 0; UnBase64(&Result, "BQ==", 4);
、スタックが破損します。
人々がc ++ソリューションを必要とする場合、私はこのOpenSSLソリューションを(エンコードとデコードの両方のために)まとめました。「crypto」ライブラリ(OpenSSL)とリンクする必要があります。これは、valgrindを使用してリークがチェックされています(エラーチェックコードを追加して少し良くすることもできますが、少なくとも書き込み関数が戻り値をチェックする必要があることはわかっています)。
#include <openssl/bio.h>
#include <openssl/evp.h>
#include <stdlib.h>
string base64_encode( const string &str ){
BIO *base64_filter = BIO_new( BIO_f_base64() );
BIO_set_flags( base64_filter, BIO_FLAGS_BASE64_NO_NL );
BIO *bio = BIO_new( BIO_s_mem() );
BIO_set_flags( bio, BIO_FLAGS_BASE64_NO_NL );
bio = BIO_push( base64_filter, bio );
BIO_write( bio, str.c_str(), str.length() );
BIO_flush( bio );
char *new_data;
long bytes_written = BIO_get_mem_data( bio, &new_data );
string result( new_data, bytes_written );
BIO_free_all( bio );
return result;
}
string base64_decode( const string &str ){
BIO *bio, *base64_filter, *bio_out;
char inbuf[512];
int inlen;
base64_filter = BIO_new( BIO_f_base64() );
BIO_set_flags( base64_filter, BIO_FLAGS_BASE64_NO_NL );
bio = BIO_new_mem_buf( (void*)str.c_str(), str.length() );
bio = BIO_push( base64_filter, bio );
bio_out = BIO_new( BIO_s_mem() );
while( (inlen = BIO_read(bio, inbuf, 512)) > 0 ){
BIO_write( bio_out, inbuf, inlen );
}
BIO_flush( bio_out );
char *new_data;
long bytes_written = BIO_get_mem_data( bio_out, &new_data );
string result( new_data, bytes_written );
BIO_free_all( bio );
BIO_free_all( bio_out );
return result;
}
私はC ++で使用するために1つ作成しました。これは非常に高速で、ストリーム、フリー、オープンソースで動作します。
https://tmplusplus.svn.sourceforge.net/svnroot/tmplusplus/trunk/src/
用途に合わせてお気軽にご利用ください。
編集:リクエストによりインラインでコードを追加しました。
パフォーマンスの向上は、エンコードとデコードにルックアップテーブルを使用することによって実現されます。_UINT8
あるunsigned char
ほとんどのOSの上で。
/** Static Base64 character encoding lookup table */
const char CBase64::encodeCharacterTable[65] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
/** Static Base64 character decoding lookup table */
const char CBase64::decodeCharacterTable[256] = {
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
,-1,62,-1,-1,-1,63,52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-1,-1,-1,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21
,22,23,24,25,-1,-1,-1,-1,-1,-1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
-1,-1,-1};
/*!
\brief Encodes binary data to base 64 character data
\param in The data to encode
\param out The encoded data as characters
*/
void CBase64::Encode(std::istream &in, std::ostringstream &out)
{
char buff1[3];
char buff2[4];
_UINT8 i=0, j;
while(in.readsome(&buff1[i++], 1))
if (i==3)
{
out << encodeCharacterTable[(buff1[0] & 0xfc) >> 2];
out << encodeCharacterTable[((buff1[0] & 0x03) << 4) + ((buff1[1] & 0xf0) >> 4)];
out << encodeCharacterTable[((buff1[1] & 0x0f) << 2) + ((buff1[2] & 0xc0) >> 6)];
out << encodeCharacterTable[buff1[2] & 0x3f];
i=0;
}
if (--i)
{
for(j=i;j<3;j++) buff1[j] = '\0';
buff2[0] = (buff1[0] & 0xfc) >> 2;
buff2[1] = ((buff1[0] & 0x03) << 4) + ((buff1[1] & 0xf0) >> 4);
buff2[2] = ((buff1[1] & 0x0f) << 2) + ((buff1[2] & 0xc0) >> 6);
buff2[3] = buff1[2] & 0x3f;
for (j=0;j<(i+1);j++) out << encodeCharacterTable[buff2[j]];
while(i++<3) out << '=';
}
}
/*!
\brief Decodes base 64 character data to binary data
\param in The character data to decode
\param out The decoded data
*/
void CBase64::Decode(std::istringstream &in, std::ostream &out)
{
char buff1[4];
char buff2[4];
_UINT8 i=0, j;
while(in.readsome(&buff2[i], 1) && buff2[i] != '=')
{
if (++i==4)
{
for (i=0;i!=4;i++)
buff2[i] = decodeCharacterTable[buff2[i]];
out << (char)((buff2[0] << 2) + ((buff2[1] & 0x30) >> 4));
out << (char)(((buff2[1] & 0xf) << 4) + ((buff2[2] & 0x3c) >> 2));
out << (char)(((buff2[2] & 0x3) << 6) + buff2[3]);
i=0;
}
}
if (i)
{
for (j=i;j<4;j++) buff2[j] = '\0';
for (j=0;j<4;j++) buff2[j] = decodeCharacterTable[buff2[j]];
buff1[0] = (buff2[0] << 2) + ((buff2[1] & 0x30) >> 4);
buff1[1] = ((buff2[1] & 0xf) << 4) + ((buff2[2] & 0x3c) >> 2);
buff1[2] = ((buff2[2] & 0x3) << 6) + buff2[3];
for (j=0;j<(i-1); j++) out << (char)buff1[j];
}
}
ryyst(投票数が最も多かった)のコードの小さな改善点は、動的に割り当てられたデコードテーブルではなく、静的なconst事前計算テーブルを使用することです。これにより、ポインターの使用とテーブルの初期化が不要になり、base64_cleanup()を使用してデコードテーブルをクリーンアップするのを忘れた場合(つまり、base64_cleanup()でfree(decoding_table)を呼び出した後)、メモリリークが回避されます。 decode_table = NULL、それ以外の場合、base64_cleanup()の後に誤ってbase64_decodeを呼び出すと、クラッシュするか、動作が不定になります)。別の解決策はstd :: unique_ptr ...を使用することですが、スタックにconst char [256]を置くだけで十分であり、ポインターをすべて一緒に使用しないようにします-コードはこのようにすっきりと短く見えます。
デコードテーブルは次のように計算されます。
const char encoding_table[] = {
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
'w', 'x', 'y', 'z', '0', '1', '2', '3',
'4', '5', '6', '7', '8', '9', '+', '/' };
unsigned char decoding_table[256];
for (int i = 0; i < 256; i++)
decoding_table[i] = '\0';
for (int i = 0; i < 64; i++)
decoding_table[(unsigned char)encoding_table[i]] = i;
for (int i = 0; i < 256; i++)
cout << "0x" << (int(decoding_table[i]) < 16 ? "0" : "") << hex << int(decoding_table[i]) << (i != 255 ? "," : "") << ((i+1) % 16 == 0 ? '\n' : '\0');
cin.ignore();
そして私が使用している変更されたコードは次のとおりです:
static const char encoding_table[] = {
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
'w', 'x', 'y', 'z', '0', '1', '2', '3',
'4', '5', '6', '7', '8', '9', '+', '/' };
static const unsigned char decoding_table[256] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x3f,
0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
char* base64_encode(const unsigned char *data, size_t input_length, size_t &output_length) {
const int mod_table[] = { 0, 2, 1 };
output_length = 4 * ((input_length + 2) / 3);
char *encoded_data = (char*)malloc(output_length);
if (encoded_data == nullptr)
return nullptr;
for (int i = 0, j = 0; i < input_length;) {
uint32_t octet_a = i < input_length ? (unsigned char)data[i++] : 0;
uint32_t octet_b = i < input_length ? (unsigned char)data[i++] : 0;
uint32_t octet_c = i < input_length ? (unsigned char)data[i++] : 0;
uint32_t triple = (octet_a << 0x10) + (octet_b << 0x08) + octet_c;
encoded_data[j++] = encoding_table[(triple >> 3 * 6) & 0x3F];
encoded_data[j++] = encoding_table[(triple >> 2 * 6) & 0x3F];
encoded_data[j++] = encoding_table[(triple >> 1 * 6) & 0x3F];
encoded_data[j++] = encoding_table[(triple >> 0 * 6) & 0x3F];
}
for (int i = 0; i < mod_table[input_length % 3]; i++)
encoded_data[output_length - 1 - i] = '=';
return encoded_data;
};
unsigned char* base64_decode(const char *data, size_t input_length, size_t &output_length) {
if (input_length % 4 != 0)
return nullptr;
output_length = input_length / 4 * 3;
if (data[input_length - 1] == '=') (output_length)--;
if (data[input_length - 2] == '=') (output_length)--;
unsigned char* decoded_data = (unsigned char*)malloc(output_length);
if (decoded_data == nullptr)
return nullptr;
for (int i = 0, j = 0; i < input_length;) {
uint32_t sextet_a = data[i] == '=' ? 0 & i++ : decoding_table[data[i++]];
uint32_t sextet_b = data[i] == '=' ? 0 & i++ : decoding_table[data[i++]];
uint32_t sextet_c = data[i] == '=' ? 0 & i++ : decoding_table[data[i++]];
uint32_t sextet_d = data[i] == '=' ? 0 & i++ : decoding_table[data[i++]];
uint32_t triple = (sextet_a << 3 * 6)
+ (sextet_b << 2 * 6)
+ (sextet_c << 1 * 6)
+ (sextet_d << 0 * 6);
if (j < output_length) decoded_data[j++] = (triple >> 2 * 8) & 0xFF;
if (j < output_length) decoded_data[j++] = (triple >> 1 * 8) & 0xFF;
if (j < output_length) decoded_data[j++] = (triple >> 0 * 8) & 0xFF;
}
return decoded_data;
};
これは、putchar関数に直接書き込むことにより、バッファーの必要性を回避するために特別に記述されたデコーダーです。これはwikibookの実装に基づいていますhttps://en.wikibooks.org/wiki/Algorithm_Implementation/Miscellaneous/Base64#C
これは、上記の他のオプションほど簡単に使用できません。ただし、結果のbase64 datauri文字列を格納するために別の大きなバッファーを割り当てずに大きなファイルをダンプしたい組み込みシステムで使用できます。(datauriでファイル名を指定できないのは残念です)。
void datauriBase64EncodeBufferless(int (*putchar_fcptr)(int), const char* type_strptr, const void* data_buf, const size_t dataLength)
{
const char base64chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
const uint8_t *data = (const uint8_t *)data_buf;
size_t x = 0;
uint32_t n = 0;
int padCount = dataLength % 3;
uint8_t n0, n1, n2, n3;
size_t outcount = 0;
size_t line = 0;
putchar_fcptr((int)'d');
putchar_fcptr((int)'a');
putchar_fcptr((int)'t');
putchar_fcptr((int)'a');
putchar_fcptr((int)':');
outcount += 5;
while (*type_strptr != '\0')
{
putchar_fcptr((int)*type_strptr);
type_strptr++;
outcount++;
}
putchar_fcptr((int)';');
putchar_fcptr((int)'b');
putchar_fcptr((int)'a');
putchar_fcptr((int)'s');
putchar_fcptr((int)'e');
putchar_fcptr((int)'6');
putchar_fcptr((int)'4');
putchar_fcptr((int)',');
outcount += 8;
/* increment over the length of the string, three characters at a time */
for (x = 0; x < dataLength; x += 3)
{
/* these three 8-bit (ASCII) characters become one 24-bit number */
n = ((uint32_t)data[x]) << 16; //parenthesis needed, compiler depending on flags can do the shifting before conversion to uint32_t, resulting to 0
if((x+1) < dataLength)
n += ((uint32_t)data[x+1]) << 8;//parenthesis needed, compiler depending on flags can do the shifting before conversion to uint32_t, resulting to 0
if((x+2) < dataLength)
n += data[x+2];
/* this 24-bit number gets separated into four 6-bit numbers */
n0 = (uint8_t)(n >> 18) & 63;
n1 = (uint8_t)(n >> 12) & 63;
n2 = (uint8_t)(n >> 6) & 63;
n3 = (uint8_t)n & 63;
/*
* if we have one byte available, then its encoding is spread
* out over two characters
*/
putchar_fcptr((int)base64chars[n0]);
putchar_fcptr((int)base64chars[n1]);
outcount += 2;
/*
* if we have only two bytes available, then their encoding is
* spread out over three chars
*/
if((x+1) < dataLength)
{
putchar_fcptr((int)base64chars[n2]);
outcount += 1;
}
/*
* if we have all three bytes available, then their encoding is spread
* out over four characters
*/
if((x+2) < dataLength)
{
putchar_fcptr((int)base64chars[n3]);
outcount += 1;
}
/* Breaking up the line so it's easier to copy and paste */
int curr_line = (outcount/80);
if( curr_line != line )
{
line = curr_line;
putchar_fcptr((int)'\r');
putchar_fcptr((int)'\n');
}
}
/*
* create and add padding that is required if we did not have a multiple of 3
* number of characters available
*/
if (padCount > 0)
{
for (; padCount < 3; padCount++)
{
putchar_fcptr((int)'=');
}
}
putchar_fcptr((int)'\r');
putchar_fcptr((int)'\n');
}
これがテストです
#include <stdio.h>
#include <stdint.h>
#include <string.h>
int main(void)
{
char str[] = "test";
datauriBase64EncodeBufferless(putchar, "text/plain;charset=utf-8", str, strlen(str));
return 0;
}
期待される出力: data:text/plain;charset=utf-8;base64,dGVzdA==
機能、それは非常に簡単になります:EVP_EncodeBlock
EVP_DecodeBlock
#include <stdio.h>
#include <stdlib.h>
#include <openssl/evp.h>
char *base64(const unsigned char *input, int length) {
const int pl = 4*((length+2)/3);
char *output = calloc(pl+1, 1); //+1 for the terminating null that EVP_EncodeBlock adds on
const int ol = EVP_EncodeBlock(output, input, length);
if (ol != pl) { fprintf(stderr, "Whoops, encode predicted %d but we got %d\n", pl, ol); }
return output;
}
unsigned char *decode64(const char *input, int length) {
const int pl = 3*length/4;
unsigned char *output = calloc(pl+1, 1);
const int ol = EVP_DecodeBlock(output, input, length);
if (pl != ol) { fprintf(stderr, "Whoops, decode predicted %d but we got %d\n", pl, ol); }
return output;
}
このソリューションはシュルヴィッツの回答(OpenSSLを使用したエンコード/デコード)に基づいていますが、C ++の場合(まあ、元の質問はCに関するものでしたが、すでに別のC ++の回答がここにあります)、エラーチェックを使用しています(そのため、より安全です) :
#include <openssl/bio.h>
std::string base64_encode(const std::string &input)
{
BIO *p_bio_b64 = nullptr;
BIO *p_bio_mem = nullptr;
try
{
// make chain: p_bio_b64 <--> p_bio_mem
p_bio_b64 = BIO_new(BIO_f_base64());
if (!p_bio_b64) { throw std::runtime_error("BIO_new failed"); }
BIO_set_flags(p_bio_b64, BIO_FLAGS_BASE64_NO_NL); //No newlines every 64 characters or less
p_bio_mem = BIO_new(BIO_s_mem());
if (!p_bio_mem) { throw std::runtime_error("BIO_new failed"); }
BIO_push(p_bio_b64, p_bio_mem);
// write input to chain
// write sequence: input -->> p_bio_b64 -->> p_bio_mem
if (BIO_write(p_bio_b64, input.c_str(), input.size()) <= 0)
{ throw std::runtime_error("BIO_write failed"); }
if (BIO_flush(p_bio_b64) <= 0)
{ throw std::runtime_error("BIO_flush failed"); }
// get result
char *p_encoded_data = nullptr;
auto encoded_len = BIO_get_mem_data(p_bio_mem, &p_encoded_data);
if (!p_encoded_data) { throw std::runtime_error("BIO_get_mem_data failed"); }
std::string result(p_encoded_data, encoded_len);
// clean
BIO_free_all(p_bio_b64);
return result;
}
catch (...)
{
if (p_bio_b64) { BIO_free_all(p_bio_b64); }
throw;
}
}
std::string base64_decode(const std::string &input)
{
BIO *p_bio_mem = nullptr;
BIO *p_bio_b64 = nullptr;
try
{
// make chain: p_bio_b64 <--> p_bio_mem
p_bio_b64 = BIO_new(BIO_f_base64());
if (!p_bio_b64) { throw std::runtime_error("BIO_new failed"); }
BIO_set_flags(p_bio_b64, BIO_FLAGS_BASE64_NO_NL); //Don't require trailing newlines
p_bio_mem = BIO_new_mem_buf((void*)input.c_str(), input.length());
if (!p_bio_mem) { throw std::runtime_error("BIO_new failed"); }
BIO_push(p_bio_b64, p_bio_mem);
// read result from chain
// read sequence (reverse to write): buf <<-- p_bio_b64 <<-- p_bio_mem
std::vector<char> buf((input.size()*3/4)+1);
std::string result;
for (;;)
{
auto nread = BIO_read(p_bio_b64, buf.data(), buf.size());
if (nread < 0) { throw std::runtime_error("BIO_read failed"); }
if (nread == 0) { break; } // eof
result.append(buf.data(), nread);
}
// clean
BIO_free_all(p_bio_b64);
return result;
}
catch (...)
{
if (p_bio_b64) { BIO_free_all(p_bio_b64); }
throw;
}
}
入力が不正なbase64シーケンスの場合、base64_decodeは空の文字列を返すことに注意してください(opensslはこのように機能します)。
これは、受け入れられた回答用に最適化されたバージョンのエンコーダーであり、MIMEおよびその他のプロトコルの改行もサポートしています(同様の最適化をデコーダーに適用できます)。
char *base64_encode(const unsigned char *data,
size_t input_length,
size_t *output_length,
bool addLineBreaks)
*output_length = 4 * ((input_length + 2) / 3);
if (addLineBreaks) *output_length += *output_length / 38; // CRLF after each 76 chars
char *encoded_data = malloc(*output_length);
if (encoded_data == NULL) return NULL;
UInt32 octet_a;
UInt32 octet_b;
UInt32 octet_c;
UInt32 triple;
int lineCount = 0;
int sizeMod = size - (size % 3); // check if there is a partial triplet
// adding all octet triplets, before partial last triplet
for (; offset < sizeMod; )
{
octet_a = data[offset++];
octet_b = data[offset++];
octet_c = data[offset++];
triple = (octet_a << 0x10) + (octet_b << 0x08) + octet_c;
encoded_data[mBufferPos++] = encoding_table[(triple >> 3 * 6) & 0x3F];
encoded_data[mBufferPos++] = encoding_table[(triple >> 2 * 6) & 0x3F];
encoded_data[mBufferPos++] = encoding_table[(triple >> 1 * 6) & 0x3F];
encoded_data[mBufferPos++] = encoding_table[(triple >> 0 * 6) & 0x3F];
if (addLineBreaks)
{
if (++lineCount == 19)
{
encoded_data[mBufferPos++] = 13;
encoded_data[mBufferPos++] = 10;
lineCount = 0;
}
}
}
// last bytes
if (sizeMod < size)
{
octet_a = data[offset++]; // first octect always added
octet_b = offset < size ? data[offset++] : (UInt32)0; // conditional 2nd octet
octet_c = (UInt32)0; // last character is definitely padded
triple = (octet_a << 0x10) + (octet_b << 0x08) + octet_c;
encoded_data[mBufferPos++] = encoding_table[(triple >> 3 * 6) & 0x3F];
encoded_data[mBufferPos++] = encoding_table[(triple >> 2 * 6) & 0x3F];
encoded_data[mBufferPos++] = encoding_table[(triple >> 1 * 6) & 0x3F];
encoded_data[mBufferPos++] = encoding_table[(triple >> 0 * 6) & 0x3F];
// add padding '='
sizeMod = size % 3;
// last character is definitely padded
encoded_data[mBufferPos - 1] = (byte)'=';
if (sizeMod == 1) encoded_data[mBufferPos - 2] = (byte)'=';
}
}