Determining size of vsprintf output - c++

I am implementing a printf(const char * format, ...) like method for a class, where I need to determine the exact size of it's output given only the supplied format and the arguments given in va_list, before calling vsprintf() to perform the actual writing.
Is there a function that can take the format and va_list to generate the exact length of the output?

As from the example in the documentation, you can determine the necessary buffer size 1st (emphasis mine):
std::vector<char> buf(1+std::vsnprintf(NULL, 0, fmt, args1)); // <<<
va_end(args1);
std::vsnprintf(buf.data(), buf.size(), fmt, args2);
4) Writes the results to a character string buffer. At most buf_size-1 characters are written. The resulting character string will be terminated with a null character, unless buf_size is zero. If buf_size is zero, nothing is written and buffer may be a null pointer, however the return value (number of bytes that would be written not including the null terminator) is still calculated and returned.
Generally all variants of the <x>sprintf() functions family can be used to calculate the necessary buffer size if the target buffer parameter is passed as NULL or nullptr.

Related

sprintf() is adding an extra variable

Why is this happening?:
char buf[256];
char date[8];
sprintf(date, "%d%02d%02d", Time.year(), Time.month(), Time.day());
snprintf(buf, sizeof(buf), "{\"team\":\"%s\"}", team.c_str());
Serial.println(date);
output:
20180202{"team":"IND"}
it should only be: 20180202
I don't know why {"team":"IND"} is getting added to the end of it.
Very likely you declared two arrays and they are lined up in a way that allowed for the buf to overwrite the null terminator of date and thus it's "concatenating" the two.
I can't write code to reproduce this because it's undefined behavior and thus not reliable. But I can tell you how you can avoid it,
snprintf(date, sizeof(date), "%d%02d%02d", Time.year(), Time.month(), Time.day());
snprintf(buf, sizeof(buf), "{\"team\":\"%s\"}", team.c_str());
Having said that, why are you using snprintf() when this appears to be c++? And so there are more suitable solutions for this kind of problem.
This would print an incorrect value, but would not cause any unexpected behavior.
Strings in c are simply arrays with a special arrangement. If the string has n printable characters it should be stored in an array of size n + 1, so that you can add what is called a null terminator. It's a special value that indicates the end of the string.
Your second snprintf() is overwriting such null terminator of the date array and thus appearing to concatenate both strings.
You have reserved space to store exactly 8 chars:
char date[8];
To store the date properly 20180202 you need
char date[9];
because sprintf() puts the extra '\0' character to the buffer you pass for proper c-style string termination.
I'd suspect you declared your buffers like
char buffer[???];
char date[8];
since these are most likely stored on your processors stack, you need to read that backwards, thus the output placed at buffer overwrites that terminating '\0', and appears immediately after date.

Why wstring can accept WCHAR[] while string doesn't accept UCHAR[]

I am trying to print the returned value of NtQueryValueKey which is UCHAR Data[1]; i have tried printf, cout, and string(Data, DataLengh), with the first two printing only 1 character and the last one throws an exception... Basically if i changed the Data Type to WCHAR Data[1] and used wstring(Data) it accepts it normally without any complain... also wprintf prints the value normally.
Edit: I meant NtQueryValueKey using the KEY_VALUE_PARTIAL_INFORMATION, I am using VS 2015 btw...
You must have mixed something up. You did not specify what value from the KEY_NAME_INFORMATION enumeration you are using for the second parameter to specify the data type, but a quick look at MSDN shows that all of the structures contain WCHAR Name[1]; or something similar as the last member (which I guess is the one you are interested in). Can you elaborate and provide the link or other means of documentation that states you actually need to use UCHAR ?
WCHAR is an alias for wchar_t. std::wstring operates with wchar_t elements. A WCHAR[] can decay to a wchar_t*, and thus can be assigned directly to a std::wstring.
UCHAR is an alias for unsigned char. std::string operates with char elements instead. A UCHAR[]/UCHAR* cannot be assigned directly to a std::string without a type-cast to char*, as char and unsigned char are distinct data types.
unsigned char is commonly used to represent 8bit bytes (it is the same data type used for BYTE).
NtQueryKey() returns strings as UTF-16LE encoded bytes using WCHAR[] character arrays, not UCHAR[] byte arrays. So your code is declaring things wrong if you are using UCHAR[] to begin with. But even so, you can use UCHAR if you pay attention to the encoding and byte length, and use appropriate type-casts.
Any associated Length value reported by NtQueryKey() is expressed in bytes, not characters. sizeof(UCHAR) is 1 and sizeof(WCHAR) is 2. So every 2 UCHARs represents 1 WCHAR. And the strings are not null-terminated, so you have to take the Length into account when printing or converting.
In Latin-based languages, most commonly used Unicode characters will be <= U+00FF, and thus every other UCHAR in UTF-16LE will usually be 0. That is interpreted as a null terminator when UTF-16 is printed with printf() or std::cout. You need to use wprintf() or std::wcout instead.
Converting Data to a std::string is a valid operation and should not be raising an exception:
std::string((char*)Data, DataLength)
Provided that:
Data is a valid pointer.
DataLength is an accurate byte count.
The only way this could raise an exception is if either:
Data is not pointing at valid memory.
the value of DataLength is more than the actual number of bytes allocated for Data.
available memory is too low to allocate std::string's internal buffer.
memory is corrupted.
Assigning Data by itself to a std::wstring without taking DataLength into account is not a valid operation because the strings are not null-terminated. You must specify the length:
std::wstring(Data, DataLength / sizeof(WCHAR))
If Data is UCHAR then use a type-cast:
std::wstring((WCHAR*)Data, DataLength / sizeof(WCHAR))
When printing Data directly with wprintf(), you must pass DataLength as an input parameter:
wprintf(L"%.*s", DataLength / sizeof(WCHAR), Data);
When printing Data directly with std::wcout, you should use write() instead of operator<< so you can pass DataLength as an input parameter:
std::wcout.write(Data, DataLength / sizeof(WCHAR));
If Data is UCHAR then use a type-cast:
std::wcout.write((WCHAR*)Data, DataLength / sizeof(WCHAR));

String copy with strcpy_s

Trying to copy string using strcpy_s
char foo[10]; // a buffer able to hold 9 chars (plus the null)
char bar[] = "A string longer than 9 chars";
strcpy_s( foo, 10, bar );
Got Assertion:
Expression: (L"Buffer is too small" && 0)
Can't understand why. foo has space for 10 characters and second parameter is 10. So, what is wrong?
This is the expected behaviour
in docs you have:
numberOfElements
Size of the destination string buffer in char units for narrow and multi-byte functions, and wchar_t units for wide functions.
so second parametr is the size of your destination buffer. And function tries to copy all characters from the source.
If you want to copy as many as possible elements using *_s function then consider using:
strncpy_s(foo, sizeof(foo)/sizeof(foo[0]), bar, _TRUNCATE);
errno_t strcpy_s(
char *strDestination,
size_t numberOfElements,
const char *strSource
);
The strcpy_s function copies the contents in the address of strSource, including the terminating null character, to the location that's specified by strDestination. The destination string must be large enough to hold the source string and its terminating null character. The behavior of strcpy_s is undefined if the source and destination strings overlap.
From MSDN documentation, if the destination is not large enough, you will get error.
You are writing as much characters as the buffer could contain. As stated in the documentation:
2) Same as (1), except that it may clobber the rest of the destination
array with unspecified values and that the following errors are
detected at runtime and call the currently installed constraint
handler function:
[...]
destsz is less or equal strnlen_s(src, destsz); in other words, truncation would occur
[...]
You fall in the "equal" case, thus the error. If you known what you do, you can ignore it or enable truncation.

RegKeyValue returning nonsense data

char value[255];
DWORD BufferSize = 8192;
RegGetValue(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion", L"ProductName", RRF_RT_ANY, NULL, &value, &BufferSize);
cout << value;
After RegKeyValue() runs, it appears that value is
value 0x0034f50c "ÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌ... char[255]
What's going on here?
Note: RegKeyValue() returns 0
There are two issues here.
Make sure the return value of RegGetValue is ERROR_SUCCESS. If it is not, the routine failed. Also, you can check to see what was written into BufferSize, as RegGetValue specifies the number of bytes written.
You're passing in a buffer defined as char value[255];, then specifying it's length as 8192. This can cause a buffer overrun.
You didn't check the return value of RegGetValue. Most likely the call failed and the buffer value was never assigned anything. Always check return values.
From the code we can see, I note that you are lying about the buffer size. You say that it is 8192 bytes. But you only allocated 255 bytes. You are also calling the Unicode version of the API, but passing in a char buffer. If you are expecting string data then you need to supply a buffer of wide characters. The Unicode version of this API will return string data as UTF-16 encoded text.
Once you get all that sorted you next need to check what type is stored in that value. You are passing NULL for the type parameter. Pass a pointer to a variable and find out whether or not a string really is stored
there. You will also need to read how many bytes are read and set the null-terminator in your buffer accordingly.

DTrace: how to print out memory buffers

I need to trace all pwrite(2) calls done to some specific file and print all buffers that are to be written byte by byte. The data expected is not in ASCII string format so i can't use:
printf("%s\n", copyinstr(arg1))
I learned about copyin(arg1, arg2), but that gives me a void* and all examples i've found covert it to string again by calling stringof, e.g.:
printf("%s\n", stringof(copyin(arg1, arg2)))
What i need is to somehow display all bytes in copyin'ed buffer. Any suggestions?
Thank you,
Inso.
Ok, it's done with tracemem(buffer, size) call.
tracemem(address, size_t nbytes, size_t dbytes), where nbytes is a constant and dbytes can be dynamic and less than nbytes.
tracemem
void tracemem(address, size_t nbytes)
void tracemem(address, size_t nbytes, size_t dbytes)
The tracemem action takes a D expression as its first argument,
address, and a constant as its second argument, nbytes. tracemem
copies the memory from the address specified by addr into the directed
buffer for the length specified by nbytes.
If the third argument, dbytes is supplied, only up to dbytes will be
copied. dbytes is allowed to be a variable amount, but it must be less
than or equal to nbytes. This is useful when you are looking at
something that has a known upper bound, but the actual number of bytes
may vary. For example, consider the case where you are dumping an
Ethernet packet. The maximum size is based on the MTU, but the
amount of data in the given packet is variable.