为什么在调试语句 Serial.println(i) 时 for 循环行为会发生变化;存在与被注释掉
Why does for loop behavior change when debug statement Serial.println(i); is present vs. commented out
我已经为 Arduino IDE 编写了一个 base 64 编码/解码库(是的,我知道这样的库已经存在。这既是为了我自己的教育,也是为了任何实用的东西)。我的目标微控制器是 Espressif ESP32。
在我的 base64 解码函数中,索引变量 i
受到 Serial.println()
调试语句的影响。这是我见过的最奇怪的事情,我不明白为什么不管是否有调试打印都会有所不同。
使用下面的测试程序 base64.ino
和 b64.cpp
函数,这是我的串行输出的两个示例。在第一个示例中,我在 for 循环之后的函数 b64dec()
中使用了 Serial.println(i);
。在第二个示例中,Serial.println(i);
被注释掉了。这是唯一的区别,我得到了两个截然不同的结果。
我是不是漏掉了什么?是不是编译器优化出了问题?我对C变量作用域的理解是只有全局、函数和参数级别。 for 循环中的 i
应该与它上面几行的 int i = 0;
相同。我也不相信它是缓冲区溢出,因为解码长度的调试输出 13 对于 12 个字符的消息及其 NULL 终止符是准确的。
这是我每次 运行 都希望得到的:
Hello World!
...is encoded as...
SGVsbG8gV29ybGQhAA==
SGVsbG8gV29ybGQhAA==
Size of encoded message (including NULL terminator): 21
Bytes needed for decoded message: 13
Looping until i < 15
0
4
8
12
Bytes decoded: 13
72 101 108 108 111 32 87 111 114 108 100 33 0
Decoded message: Hello World!
这是 Serial.println(i);
被注释掉后得到的结果:
Hello World!
...is encoded as...
SGVsbG8gV29ybGQhAA==
SGVsbG8gV29ybGQhAA==
Size of encoded message (including NULL terminator): 21
Bytes needed for decoded message: 13
Looping until i < 15
Bytes decoded: 4
72 101 108 108
Decoded message: Hell
我真的被困在地狱里了。
正如您从其他调试输出中看到的那样,它应该一直循环到 i<15
,但它只循环到 4。
比较我的解码函数b64dec()
和我的编码函数b64enc()
,循环的方式非常相似。然而,b64enc()
不需要调试 Serial.println()
即可运行。
如有任何关于我可能遗漏的建议,我们将不胜感激。
代码如下:
base64.ino -- 这是调用库函数的测试程序。
#include <b64.h>
void setup() {
Serial.begin(115200);
delay(1000);
// Encoding example.
char msg1[] = "Hello World!";
char result1[b64enclen(sizeof msg1)];
b64enc(msg1, result1, sizeof msg1);
Serial.println(msg1);
Serial.println("...is encoded as...");
Serial.println(result1);
Serial.println();
// Decoding example.
char enc_msg[] = "SGVsbG8gV29ybGQhAA==";
Serial.println(enc_msg);
Serial.print("Size of encoded message (including NULL terminator): ");
Serial.println(sizeof enc_msg);
Serial.print("Bytes needed for decoded message: ");
Serial.println(b64declen(enc_msg, sizeof enc_msg));
char dec_result[b64declen(enc_msg, sizeof enc_msg)];
int declen = b64dec(enc_msg, dec_result, sizeof enc_msg);
Serial.print("Bytes decoded: ");
Serial.println(declen, DEC);
for (int k=0; k<declen; k++) {
Serial.print(dec_result[k], DEC);
Serial.print(" ");
}
Serial.println();
Serial.print("Decoded message: ");
Serial.println(dec_result);
}
void loop() {
}
b64.cpp -- 注意:这是直接的 C,但链接器不会找到它,除非它有一个 .cpp 扩展名。
/*
base64 functions for turning binary data into strings and back again.
Created April 2021 - David Horton and released to public domain.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
*/
#include <b64.h>
#include <Arduino.h>
// b64map - 6-bit index selects the correct character from the base64
// 'alphabet' as described in RFC4648. Also used for decoding functions.
const char b64map[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
// b64pad - padding character, also described in RFC4648.
const char b64pad = '=';
size_t b64enclen(size_t unenc_len) {
size_t enc_len;
// Padding the unencoded length by 2 for worst case makes calculating
// easier. If it turns out padding is not needed, integer division will
// drop the fractional part. The result is the correct length without any
// of the hassle dealing with padding.
unenc_len += 2;
// Encoded is four-thirds the unencoded length. Add 1 for NULL terminator.
enc_len = (unenc_len / 3 * 4) + 1;
return enc_len;
}
int b64enc(char *unenc, char *enc, size_t unenc_len) {
unsigned char buffer[4]; // Temp storage for mapping three bytes to four characters.
// Any input not evenly divisible by three requires padding at the end.
// Determining what remainder exists after dividing by three helps when
// dealing with those special cases.
int remainder = unenc_len %3;
// Loop through unencoded characters in sets of three at a time. Any one or
// two characters remaining are dealt with at the end to properly determine
// their padding.
int i = 0;
int j = 0;
for (i=0; i<unenc_len - remainder; i+=3) { // Minus padding remainder.
// Take three bytes of eight bits and map onto four chars of six bits.
// E.g. ABCDEFGH IJKLMNOP QRSTUVWX => 00ABCDEF 00GHIJKL 00MNOPQR 00STUVWX
buffer[0] = unenc[i] >> 2;
buffer[1] = (unenc[i] & 0B00000011) << 4 | unenc[i+1] >> 4;
buffer[2] = (unenc[i+1] & 0B00001111) << 2 | unenc[i+2] >> 6;
buffer[3] = unenc[i+2] & 0B00111111;
// Map the six-bit bytes onto the ASCII characters used in base64.
enc[j++] = b64map[buffer[0]];
enc[j++] = b64map[buffer[1]];
enc[j++] = b64map[buffer[2]];
enc[j++] = b64map[buffer[3]];
}
// The remaining characters are handled differently, because there could
// be padding. The amount of padding depends upon if there are one or two
// characters left over.
switch (remainder) {
case 2:
buffer[0] = unenc[i] >> 2;
buffer[1] = (unenc[i] & 0B00000011) << 4 | unenc[i+1] >> 4;
buffer[2] = (unenc[i+1] & 0B00001111) << 2 | unenc[i+2] >> 6;
enc[j++] = b64map[buffer[0]];
enc[j++] = b64map[buffer[1]];
enc[j++] = b64map[buffer[2]];
enc[j++] = b64pad;
break;
case 1:
buffer[0] = unenc[i] >> 2;
buffer[1] = (unenc[i] & 0B00000011) << 4;
enc[j++] = b64map[buffer[0]];
enc[j++] = b64map[buffer[1]];
enc[j++] = b64pad;
enc[j++] = b64pad;
break;
}
// Finish with a NULL terminator since the encoded result is a string.
enc[j] = '[=14=]';
return j;
}
size_t b64declen(char * enc, size_t enc_len) {
size_t dec_len;
// Any C-style string not ending with a NULL timinator is invalid.
// Rememeber to subtract one from the length due to zero indexing.
if (enc[enc_len - 1] != '[=14=]') return 0;
// Even a single byte encoded to base64 results in a for character
// string (two chars, two padding.) Anything less is invalid.
if (enc_len < 4) return 0;
// Padded base64 string lengths are always divisible by four (after
// subtracting the NULL terminator) Otherwise, they're not vaild.
if ((enc_len - 1) %4 != 0) return 0;
// Maximum decoded length is three-fourths the encoded length.
dec_len = ((enc_len - 1) / 4 * 3);
// Padding characters don't count for decoded length.
if (enc[enc_len - 2] == b64pad) dec_len--;
if (enc[enc_len - 3] == b64pad) dec_len--;
return dec_len;
}
int b64dec(char *enc, char *dec, size_t enc_len) {
unsigned char buffer[4]; // Temp storage for mapping three bytes to four characters.
// base64 encoded input should always be evenly divisible by four, due to
// padding characters. If not, it's an error. Note: because base64 is held
// in a C-style string, there's the NULL terminator to subtract first.
if ((enc_len - 1) %4 != 0) return 0;
int padded = 0;
if (enc[enc_len - 2] == b64pad) padded++;
if (enc[enc_len - 3] == b64pad) padded++;
// Loop through encoded characters in sets of four at a time, because there
// are four encoded characters for every three decoded characters. But, if
// its not evenly divisible by four leave the remaining as a special case.
int i = 0;
int j = 0;
Serial.print("Looping until i < ");
Serial.println(enc_len - padded - 4);
for (i=0; i<enc_len - padded - 4; i+=4) {
Serial.println(i); // <-- This is the line that makes all the difference.
// Take four chars of six bits and map onto four bytes of eight bits.
// E.g. 00ABCDEF 00GHIJKL 00MNOPQR 00STUVWX => ABCDEFGH IJKLMNOP QRSTUVWX
buffer[i] = strchr(b64map, enc[i]) - b64map;
buffer[i+1] = strchr(b64map, enc[i+1]) - b64map;
buffer[i+2] = strchr(b64map, enc[i+2]) - b64map;
buffer[i+3] = strchr(b64map, enc[i+3]) - b64map;
dec[j++] = buffer[i] << 2 | buffer[i+1] >> 4;
dec[j++] = buffer[i+1] << 4 | buffer[i+2] >> 2;
dec[j++] = buffer[i+2] << 6 | buffer[i+3];
}
// Take care of special case.
switch (padded) {
case 1:
buffer[i] = strchr(b64map, enc[i]) - b64map;
buffer[i+1] = strchr(b64map, enc[i+1]) - b64map;
buffer[i+2] = strchr(b64map, enc[i+2]) - b64map;
dec[j++] = buffer[i] << 2 | buffer[i+1] >> 4;
dec[j++] = buffer[i+1] << 4 | buffer[i+2] >> 2;
break;
case 2:
buffer[i] = strchr(b64map, enc[i]) - b64map;
buffer[i+1] = strchr(b64map, enc[i+1]) - b64map;
dec[j++] = buffer[i] << 2 | buffer[i+1] >> 4;
break;
}
return j;
}
b64.h
/*
base64 functions for turning binary data into strings and back again.
Created April 2021 - David Horton and released to public domain.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef b64.h
#define b64.h
#include <stddef.h>
#include <string.h>
/*
* b64enclen
* Given number of binary bytes, calculate the number of characters needed
* to represent the data as base64. Include room for null terminator, since
* the results of encoding will be a C-style string.
* Parameters:
* unenc_len - the length of the unencoded array of characters. A simple
* 'sizeof unencoded' will work.
* Returns:
* size_t number or characters required for base64 encoded message plus a
* NULL terminator. Suitable for array declarations such as:
* 'char output[b64enclen(sizeof unencoded)]'
*/
size_t b64enclen(size_t unenc_len);
/*
* b64enc
* Given an unencoded array of bytes (binary data) and its length, fill
* the encoded array with a base64 representation of the input. b64enclen()
* should be used to properly size the character array that will hold the
* encoded output.
* Parameters:
* unenc - pointer to a character array with the contents to be encoded.
* enc - pointer to a byte array that will be filled with base64 output.
* unenc_len - length of the string pointed to by unenc. Can be found with
* 'sizeof unenc'
* Returns:
* Integer representing the number of bytes encoded.
*/
int b64enc(char *unenc, char *enc, size_t unenc_len);
/*
* b64declen
* Given a base64 encoded string and its length, perform a number of
* tests to validate the string. Then, calculate the number of bytes
* needed to represent the binary data after decoding.
* Parameters:
* enc - a pointer to the base64 encoded string.
* enc_len - the length of the encoded string (i.e. 'sizeof enc'.)
* Returns:
* size_t number of characters required for the decoded message or
* 0 in the case of an invalid base64 encoded string.
*/
size_t b64declen(char * enc, size_t enc_len);
/*
* b64dec
* Given a base64 encoded string and its length, fill the decoded array
* with the decoded binary representation of the input. b64declen() should
* be used to properly size the array intended to hold the encoded output.
*/
int b64dec(char *enc, char *dec, size_t enc_len);
#endif
同样的事情发生在“你好克利夫兰!”
与Serial.println(i);
:
Hello Cleveland!
...is encoded as...
SGVsbG8gQ2xldmVsYW5kIQA=
SGVsbG8gQ2xldmVsYW5kIQA=
Size of encoded message (including NULL terminator): 25
Bytes needed for decoded message: 17
0
4
8
12
16
Bytes decoded: 17
72 101 108 108 111 32 67 108 101 118 101 108 97 110 100 33 0
Decoded message: Hello Cleveland!
注释掉 Serial.println(i);
:
Hello Cleveland!
...is encoded as...
SGVsbG8gQ2xldmVsYW5kIQA=
SGVsbG8gQ2xldmVsYW5kIQA=
Size of encoded message (including NULL terminator): 25
Bytes needed for decoded message: 17
Bytes decoded: 5
72 101 108 108 111
Decoded message: Hello▒?
您声明了一个包含 4 个字符的数组:
int b64dec(char *enc, char *dec, size_t enc_len) {
unsigned char buffer[4];
并且您将其编入索引 [3]
:
for (i=0; i<enc_len - padded - 4; i+=4) {
buffer[i] = ...
这种缓冲区溢出可能会被代码中的其他任意内容所掩盖,例如对 Serial.println()
.
的调用
我已经为 Arduino IDE 编写了一个 base 64 编码/解码库(是的,我知道这样的库已经存在。这既是为了我自己的教育,也是为了任何实用的东西)。我的目标微控制器是 Espressif ESP32。
在我的 base64 解码函数中,索引变量 i
受到 Serial.println()
调试语句的影响。这是我见过的最奇怪的事情,我不明白为什么不管是否有调试打印都会有所不同。
使用下面的测试程序 base64.ino
和 b64.cpp
函数,这是我的串行输出的两个示例。在第一个示例中,我在 for 循环之后的函数 b64dec()
中使用了 Serial.println(i);
。在第二个示例中,Serial.println(i);
被注释掉了。这是唯一的区别,我得到了两个截然不同的结果。
我是不是漏掉了什么?是不是编译器优化出了问题?我对C变量作用域的理解是只有全局、函数和参数级别。 for 循环中的 i
应该与它上面几行的 int i = 0;
相同。我也不相信它是缓冲区溢出,因为解码长度的调试输出 13 对于 12 个字符的消息及其 NULL 终止符是准确的。
这是我每次 运行 都希望得到的:
Hello World!
...is encoded as...
SGVsbG8gV29ybGQhAA==
SGVsbG8gV29ybGQhAA==
Size of encoded message (including NULL terminator): 21
Bytes needed for decoded message: 13
Looping until i < 15
0
4
8
12
Bytes decoded: 13
72 101 108 108 111 32 87 111 114 108 100 33 0
Decoded message: Hello World!
这是 Serial.println(i);
被注释掉后得到的结果:
Hello World!
...is encoded as...
SGVsbG8gV29ybGQhAA==
SGVsbG8gV29ybGQhAA==
Size of encoded message (including NULL terminator): 21
Bytes needed for decoded message: 13
Looping until i < 15
Bytes decoded: 4
72 101 108 108
Decoded message: Hell
我真的被困在地狱里了。
正如您从其他调试输出中看到的那样,它应该一直循环到 i<15
,但它只循环到 4。
比较我的解码函数b64dec()
和我的编码函数b64enc()
,循环的方式非常相似。然而,b64enc()
不需要调试 Serial.println()
即可运行。
如有任何关于我可能遗漏的建议,我们将不胜感激。
代码如下:
base64.ino -- 这是调用库函数的测试程序。
#include <b64.h>
void setup() {
Serial.begin(115200);
delay(1000);
// Encoding example.
char msg1[] = "Hello World!";
char result1[b64enclen(sizeof msg1)];
b64enc(msg1, result1, sizeof msg1);
Serial.println(msg1);
Serial.println("...is encoded as...");
Serial.println(result1);
Serial.println();
// Decoding example.
char enc_msg[] = "SGVsbG8gV29ybGQhAA==";
Serial.println(enc_msg);
Serial.print("Size of encoded message (including NULL terminator): ");
Serial.println(sizeof enc_msg);
Serial.print("Bytes needed for decoded message: ");
Serial.println(b64declen(enc_msg, sizeof enc_msg));
char dec_result[b64declen(enc_msg, sizeof enc_msg)];
int declen = b64dec(enc_msg, dec_result, sizeof enc_msg);
Serial.print("Bytes decoded: ");
Serial.println(declen, DEC);
for (int k=0; k<declen; k++) {
Serial.print(dec_result[k], DEC);
Serial.print(" ");
}
Serial.println();
Serial.print("Decoded message: ");
Serial.println(dec_result);
}
void loop() {
}
b64.cpp -- 注意:这是直接的 C,但链接器不会找到它,除非它有一个 .cpp 扩展名。
/*
base64 functions for turning binary data into strings and back again.
Created April 2021 - David Horton and released to public domain.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
*/
#include <b64.h>
#include <Arduino.h>
// b64map - 6-bit index selects the correct character from the base64
// 'alphabet' as described in RFC4648. Also used for decoding functions.
const char b64map[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
// b64pad - padding character, also described in RFC4648.
const char b64pad = '=';
size_t b64enclen(size_t unenc_len) {
size_t enc_len;
// Padding the unencoded length by 2 for worst case makes calculating
// easier. If it turns out padding is not needed, integer division will
// drop the fractional part. The result is the correct length without any
// of the hassle dealing with padding.
unenc_len += 2;
// Encoded is four-thirds the unencoded length. Add 1 for NULL terminator.
enc_len = (unenc_len / 3 * 4) + 1;
return enc_len;
}
int b64enc(char *unenc, char *enc, size_t unenc_len) {
unsigned char buffer[4]; // Temp storage for mapping three bytes to four characters.
// Any input not evenly divisible by three requires padding at the end.
// Determining what remainder exists after dividing by three helps when
// dealing with those special cases.
int remainder = unenc_len %3;
// Loop through unencoded characters in sets of three at a time. Any one or
// two characters remaining are dealt with at the end to properly determine
// their padding.
int i = 0;
int j = 0;
for (i=0; i<unenc_len - remainder; i+=3) { // Minus padding remainder.
// Take three bytes of eight bits and map onto four chars of six bits.
// E.g. ABCDEFGH IJKLMNOP QRSTUVWX => 00ABCDEF 00GHIJKL 00MNOPQR 00STUVWX
buffer[0] = unenc[i] >> 2;
buffer[1] = (unenc[i] & 0B00000011) << 4 | unenc[i+1] >> 4;
buffer[2] = (unenc[i+1] & 0B00001111) << 2 | unenc[i+2] >> 6;
buffer[3] = unenc[i+2] & 0B00111111;
// Map the six-bit bytes onto the ASCII characters used in base64.
enc[j++] = b64map[buffer[0]];
enc[j++] = b64map[buffer[1]];
enc[j++] = b64map[buffer[2]];
enc[j++] = b64map[buffer[3]];
}
// The remaining characters are handled differently, because there could
// be padding. The amount of padding depends upon if there are one or two
// characters left over.
switch (remainder) {
case 2:
buffer[0] = unenc[i] >> 2;
buffer[1] = (unenc[i] & 0B00000011) << 4 | unenc[i+1] >> 4;
buffer[2] = (unenc[i+1] & 0B00001111) << 2 | unenc[i+2] >> 6;
enc[j++] = b64map[buffer[0]];
enc[j++] = b64map[buffer[1]];
enc[j++] = b64map[buffer[2]];
enc[j++] = b64pad;
break;
case 1:
buffer[0] = unenc[i] >> 2;
buffer[1] = (unenc[i] & 0B00000011) << 4;
enc[j++] = b64map[buffer[0]];
enc[j++] = b64map[buffer[1]];
enc[j++] = b64pad;
enc[j++] = b64pad;
break;
}
// Finish with a NULL terminator since the encoded result is a string.
enc[j] = '[=14=]';
return j;
}
size_t b64declen(char * enc, size_t enc_len) {
size_t dec_len;
// Any C-style string not ending with a NULL timinator is invalid.
// Rememeber to subtract one from the length due to zero indexing.
if (enc[enc_len - 1] != '[=14=]') return 0;
// Even a single byte encoded to base64 results in a for character
// string (two chars, two padding.) Anything less is invalid.
if (enc_len < 4) return 0;
// Padded base64 string lengths are always divisible by four (after
// subtracting the NULL terminator) Otherwise, they're not vaild.
if ((enc_len - 1) %4 != 0) return 0;
// Maximum decoded length is three-fourths the encoded length.
dec_len = ((enc_len - 1) / 4 * 3);
// Padding characters don't count for decoded length.
if (enc[enc_len - 2] == b64pad) dec_len--;
if (enc[enc_len - 3] == b64pad) dec_len--;
return dec_len;
}
int b64dec(char *enc, char *dec, size_t enc_len) {
unsigned char buffer[4]; // Temp storage for mapping three bytes to four characters.
// base64 encoded input should always be evenly divisible by four, due to
// padding characters. If not, it's an error. Note: because base64 is held
// in a C-style string, there's the NULL terminator to subtract first.
if ((enc_len - 1) %4 != 0) return 0;
int padded = 0;
if (enc[enc_len - 2] == b64pad) padded++;
if (enc[enc_len - 3] == b64pad) padded++;
// Loop through encoded characters in sets of four at a time, because there
// are four encoded characters for every three decoded characters. But, if
// its not evenly divisible by four leave the remaining as a special case.
int i = 0;
int j = 0;
Serial.print("Looping until i < ");
Serial.println(enc_len - padded - 4);
for (i=0; i<enc_len - padded - 4; i+=4) {
Serial.println(i); // <-- This is the line that makes all the difference.
// Take four chars of six bits and map onto four bytes of eight bits.
// E.g. 00ABCDEF 00GHIJKL 00MNOPQR 00STUVWX => ABCDEFGH IJKLMNOP QRSTUVWX
buffer[i] = strchr(b64map, enc[i]) - b64map;
buffer[i+1] = strchr(b64map, enc[i+1]) - b64map;
buffer[i+2] = strchr(b64map, enc[i+2]) - b64map;
buffer[i+3] = strchr(b64map, enc[i+3]) - b64map;
dec[j++] = buffer[i] << 2 | buffer[i+1] >> 4;
dec[j++] = buffer[i+1] << 4 | buffer[i+2] >> 2;
dec[j++] = buffer[i+2] << 6 | buffer[i+3];
}
// Take care of special case.
switch (padded) {
case 1:
buffer[i] = strchr(b64map, enc[i]) - b64map;
buffer[i+1] = strchr(b64map, enc[i+1]) - b64map;
buffer[i+2] = strchr(b64map, enc[i+2]) - b64map;
dec[j++] = buffer[i] << 2 | buffer[i+1] >> 4;
dec[j++] = buffer[i+1] << 4 | buffer[i+2] >> 2;
break;
case 2:
buffer[i] = strchr(b64map, enc[i]) - b64map;
buffer[i+1] = strchr(b64map, enc[i+1]) - b64map;
dec[j++] = buffer[i] << 2 | buffer[i+1] >> 4;
break;
}
return j;
}
b64.h
/*
base64 functions for turning binary data into strings and back again.
Created April 2021 - David Horton and released to public domain.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef b64.h
#define b64.h
#include <stddef.h>
#include <string.h>
/*
* b64enclen
* Given number of binary bytes, calculate the number of characters needed
* to represent the data as base64. Include room for null terminator, since
* the results of encoding will be a C-style string.
* Parameters:
* unenc_len - the length of the unencoded array of characters. A simple
* 'sizeof unencoded' will work.
* Returns:
* size_t number or characters required for base64 encoded message plus a
* NULL terminator. Suitable for array declarations such as:
* 'char output[b64enclen(sizeof unencoded)]'
*/
size_t b64enclen(size_t unenc_len);
/*
* b64enc
* Given an unencoded array of bytes (binary data) and its length, fill
* the encoded array with a base64 representation of the input. b64enclen()
* should be used to properly size the character array that will hold the
* encoded output.
* Parameters:
* unenc - pointer to a character array with the contents to be encoded.
* enc - pointer to a byte array that will be filled with base64 output.
* unenc_len - length of the string pointed to by unenc. Can be found with
* 'sizeof unenc'
* Returns:
* Integer representing the number of bytes encoded.
*/
int b64enc(char *unenc, char *enc, size_t unenc_len);
/*
* b64declen
* Given a base64 encoded string and its length, perform a number of
* tests to validate the string. Then, calculate the number of bytes
* needed to represent the binary data after decoding.
* Parameters:
* enc - a pointer to the base64 encoded string.
* enc_len - the length of the encoded string (i.e. 'sizeof enc'.)
* Returns:
* size_t number of characters required for the decoded message or
* 0 in the case of an invalid base64 encoded string.
*/
size_t b64declen(char * enc, size_t enc_len);
/*
* b64dec
* Given a base64 encoded string and its length, fill the decoded array
* with the decoded binary representation of the input. b64declen() should
* be used to properly size the array intended to hold the encoded output.
*/
int b64dec(char *enc, char *dec, size_t enc_len);
#endif
同样的事情发生在“你好克利夫兰!”
与Serial.println(i);
:
Hello Cleveland!
...is encoded as...
SGVsbG8gQ2xldmVsYW5kIQA=
SGVsbG8gQ2xldmVsYW5kIQA=
Size of encoded message (including NULL terminator): 25
Bytes needed for decoded message: 17
0
4
8
12
16
Bytes decoded: 17
72 101 108 108 111 32 67 108 101 118 101 108 97 110 100 33 0
Decoded message: Hello Cleveland!
注释掉 Serial.println(i);
:
Hello Cleveland!
...is encoded as...
SGVsbG8gQ2xldmVsYW5kIQA=
SGVsbG8gQ2xldmVsYW5kIQA=
Size of encoded message (including NULL terminator): 25
Bytes needed for decoded message: 17
Bytes decoded: 5
72 101 108 108 111
Decoded message: Hello▒?
您声明了一个包含 4 个字符的数组:
int b64dec(char *enc, char *dec, size_t enc_len) {
unsigned char buffer[4];
并且您将其编入索引 [3]
:
for (i=0; i<enc_len - padded - 4; i+=4) {
buffer[i] = ...
这种缓冲区溢出可能会被代码中的其他任意内容所掩盖,例如对 Serial.println()
.