Buffering output in C without using FILE - html

I'm trying to create a full HTML result with HTTP headers with the correct size for content-length as simple as possible.
I looked at setbuf() function but it only works with streams, but I want to directly buffer the output to memory without having a disk involved. Is there a simpler function that does what I'm trying to achieve?
I know in php, one can use ob_start() to start buffering data and ob_get_contents() to load the data in memory and determine the size so is there anything like this in C that I can use?
int main(){
char *mybuf=malloc(1048576); //Allocate 1MB memory for buffer
char *mytitle="Webpage title";
char *mydesc="This is a page version";
int myver=2;
//print HTTP header of known data
printf("HTTP/1.1 200 OK\r\n");
printf("Server: Something\r\n");
printf("Content-type: text/html; charset=UTF-8\r\n");
printf("Cache-control: no-cache\r\n");
//Buffer HTML output to get size (we don't print these 4 lines yet)
sprintf(mybuf,"<html><head><title>%s</title></head><body>\r\n",mytitle);
sprintf(mybuf+strlen(mybuf),"<h1>%s</h1>\r\n",mytitle);
sprintf(mybuf+strlen(mybuf),"<p>%s %d</p>\r\n",mydesc,myver);
sprintf(mybuf+strlen(mybuf),"</body></html>\r\n");
//Print content length header with size of buffered data
printf("Content-length: %d\r\n",strlen(mybuf));
//then print the rest of the data
printf("%s",mybuf);
free(mybuf);
return 0;
}

The C language specification does not define any facility for streams backed only by memory, without any underlying file. Your sprintf() based approach is not bad, but it could be improved by capturing and using the return value, which tells you how many characters were printed. You could use this to avoid all the strlen() calls, which could be a considerable savings if the content is large, especially if it is printed in many small pieces:
size_t total_bytes_written = 0;
long rval;
rval = sprintf(mybuf, "<html><head><title>%s</title></head><body>\r\n", mytitle);
if (rval < 0) {
// handle error ...
}
total_bytes_written += rval;
rval = sprintf(mybuf + total_bytes_written, "<h1>%s</h1>\r\n", mytitle);
// ...
Alternatively, if you are on a system that conforms to POSIX.1-2008 or later, and you are willing to limit your program to such systems, then you might find open_memstream() of interest. This gives you a stream backed by a dynamically managed block of memory. You would write to it with fprintf() or other stream IO functions, and when done, read back a pointer to the data and the total size. Something like this:
char *buf_ptr = NULL;
size_t buf_size = 0;
FILE *mem = open_memstream(&buf_ptr, &buf_size);
fprintf(mem, "<html><head><title>%s</title></head><body>\r\n", mytitle);
fprintf(mem, "<h1>%s</h1>\r\n", mytitle);
// ...
fclose(mem); // mandatory
printf("Content-length: %d\r\n", buf_size);
printf("%s", buf_ptr); // the data are guaranteed null-terminated
free(buf); // you are responsible for this
(All error checks omitted for brevity.)
This relieves you of the manual size tracking, and it gains you a dynamically-growing backing buffer so that you can handle both large and small outputs with reasonable memory efficiency.

There are multiple solutions to this problem:
your approach is OK, but you should use snprintf() to avoid potential buffer overflows:
int main() {
char *mybuf = malloc(1048576); //Allocate 1MB memory for buffer
const char *mytitle = "Webpage title";
const char *mydesc = "This is a page version";
int myver = 2;
size_t pos = 0;
//print HTTP header of known data
printf("HTTP/1.1 200 OK\r\n");
printf("Server: Something\r\n");
printf("Content-type: text/html; charset=UTF-8\r\n");
printf("Cache-control: no-cache\r\n");
//Buffer HTML output to get size (we don't print these 4 lines yet)
pos += snprintf(mybuf + pos, pos < sizeof mybuf ? sizeof mybuf - pos : 0,
"<html><head><title>%s</title></head><body>\r\n", mytitle);
pos += snprintf(mybuf + pos, pos < sizeof mybuf ? sizeof mybuf - pos : 0,
"<h1>%s</h1>\r\n", mytitle);
pos += snprintf(mybuf + pos, pos < sizeof mybuf ? sizeof mybuf - pos : 0,
"<p>%s %d</p>\r\n", mydesc, myver);
pos += snprintf(mybuf + pos, pos < sizeof mybuf ? sizeof mybuf - pos : 0,
"</body></html>\r\n");
//Print content length header with size of buffered data
printf("Content-length: %zu\r\n", pos);
printf("\r\n");
//then print the rest of the data
printf("%s", mybuf);
free(mybuf);
return 0;
}
Here is a simpler one, combining the calls and using snprintf to compute the length:
int main() {
const char *mytitle = "Webpage title";
const char *mydesc = "This is a page version";
int myver = 2;
size_t length = 0;
//print HTTP header of known data
printf("HTTP/1.1 200 OK\r\n");
printf("Server: Something\r\n");
printf("Content-type: text/html; charset=UTF-8\r\n");
printf("Cache-control: no-cache\r\n");
//Buffer HTML output to get size (we don't print these 4 lines yet)
length = snprintf(NULL, 0,
"<html><head><title>%s</title></head><body>\r\n"
"<h1>%s</h1>\r\n"
"<p>%s %d</p>\r\n"
"</body></html>\r\n",
mytitle, mytitle, mydesc, myver);
//Print content length header with size of buffered data
printf("Content-length: %zu\r\n", length);
printf("\r\n");
//then print the rest of the data
printf("<html><head><title>%s</title></head><body>\r\n"
"<h1>%s</h1>\r\n"
"<p>%s %d</p>\r\n"
"</body></html>\r\n",
mytitle, mytitle, mydesc, myver);
return 0;
}
you could use open_memstream() as explained by John Bollinger:
int main() {
const char *mytitle = "Webpage title";
const char *mydesc = "This is a page version";
int myver = 2;
size_t length = 0;
char *buf_ptr = NULL;
size_t buf_size = 0; // the buffer size
int length = 0; // the length of the output
FILE *mem = open_memstream(&buf_ptr, &buf_size);
//print HTTP header of known data
printf("HTTP/1.1 200 OK\r\n");
printf("Server: Something\r\n");
printf("Content-type: text/html; charset=UTF-8\r\n");
printf("Cache-control: no-cache\r\n");
//Buffer HTML output to get size (we don't print these 4 lines yet)
if (mem == NULL) {
// Deal with the error
printf("Internal error\r\n");
return 1;
}
length = fprintf(mem,
"<html><head><title>%s</title></head><body>\r\n"
"<h1>%s</h1>\r\n"
"<p>%s %d</p>\r\n"
"</body></html>\r\n",
mytitle, mytitle, mydesc, myver);
//Print content length header with size of buffered data
printf("Content-length: %zu\r\n", length);
printf("\r\n");
//then print the rest of the data
//beware that buf_ptr is not necessarily null terminated
printf("%.*s", length, buf_ptr);
//could also use fwrite(f, buf_ptr, 1, length, stdout);
free(buf_ptr);
return 0;
}
you can implement your own mem_printf() that allocates or reallocates a memory buffer:
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int mem_printf(char **bufp, size_t *lenp, const char *fmt, ...) {
char buf[1024];
va_list ap;
char *newbuf;
int len;
va_start(ap, fmt);
len = vsnprintf(buf, sizeof buf, fmt, ap);
va_end(ap);
if (len < 0)
return -1;
newbuf = realloc(*bufp, *lenp + len + 1);
if (newbuf == NULL)
return -1;
*bufp = newbuf;
if ((size_t)len < sizeof buf) {
memcpy(newbuf + *lenp, buf, len + 1);
} else {
va_start(ap, fmt);
vsnprintf(newbuf + *lenp, len + 1, fmt, ap);
va_end(ap);
}
*lenp += len;
return len;
}
int main() {
const char *mytitle = "Webpage title";
const char *mydesc = "This is a page version";
int myver = 2;
char *buf_ptr = NULL;
size_t buf_len = 0;
//print HTTP header of known data
printf("HTTP/1.1 200 OK\r\n");
printf("Server: Something\r\n");
printf("Content-type: text/html; charset=UTF-8\r\n");
printf("Cache-control: no-cache\r\n");
//Buffer HTML output to get size (we don't print these 4 lines yet)
mem_printf(&buf_ptr, &buf_len,
"<html><head><title>%s</title></head><body>\r\n"
"<h1>%s</h1>\r\n"
"<p>%s %d</p>\r\n"
"</body></html>\r\n",
mytitle, mytitle, mydesc, myver);
//Print content length header with size of buffered data
printf("Content-length: %zu\r\n", buf_len);
printf("\r\n");
//then print the rest of the data
fputs(buf_ptr, stdout);
free(buf_ptr);
return 0;
}

Content-Length, as you probably know, is the size of your body. This means you have to know at least the length of the body before you can generate the header (or use chunked transfer). sprintf returns the number of bytes written, either accumulate those, or squash it into a single call:
int n = snprintf(mybuf,
mybuf_len,
"<html><head><title>%s</title></head><body>\r\n"
...
"</body></html>\r\n",
mytitle, ..., myver
);
Then generate the header(n) with the n you figured out. If you pass in NULL for instead of mybuf, you get a size without writing anything, then you could do another write a pass in header with the count you got from the first call. If you want, wrap this in a function or two:
int response(const char *header, int body_len, const char body[body_len]) {
...
}
Consider picking a template library (I like the mustache format). It's kinda messy and error prune doing this by hand with raw strings. You have a 1 MB buffer allocated there but use like a .5k, and you will shoot yourself in the foot one day anyhow.

Related

Autokey Encryption

I am working on a project to write to and read from a TP Link / Kaza power strip or smart plug.
The data that is sent is encrypted json that has been "autokey encrypted".
So far I have been able to convert a typescript encrypt function and it works well. I get the expected result. However, I need to add a "header" to my encrypted data. That data is 3 null bytes followed by a byte that is a measure of the length of the encrypted bytes.
The typescript example has this bit of code to "encrypt with headers", however, I've hit a bit of a wall trying to convert it to something usable. Can someone nudge me along the path ?
First are the two typescript functions: (borrowed from https://github.com/plasticrake/tplink-smarthome-crypto/blob/master/src/index.ts)
/**
* Encrypts input where each byte is XOR'd with the previous encrypted byte.
*
* #param input - Data to encrypt
* #param firstKey - Value to XOR first byte of input
* #returns encrypted buffer
*/
export function encrypt(input: Buffer | string, firstKey = 0xab): Buffer {
const buf = Buffer.from(input);
let key = firstKey;
for (let i = 0; i < buf.length; i += 1) {
// eslint-disable-next-line no-bitwise
buf[i] ^= key;
key = buf[i];
}
return buf;
}
/**
* Encrypts input that has a 4 byte big-endian length header;
* each byte is XOR'd with the previous encrypted byte.
*
* #param input - Data to encrypt
* #param firstKey - Value to XOR first byte of input
* #returns encrypted buffer with header
*/
export function encryptWithHeader(
input: Buffer | string,
firstKey = 0xab
): Buffer {
const msgBuf = encrypt(input, firstKey);
const outBuf = Buffer.alloc(msgBuf.length + 4);
outBuf.writeUInt32BE(msgBuf.length, 0);
msgBuf.copy(outBuf, 4);
return outBuf;
}
Second is what I have so far.
// This part works well and produces the expected results
String encrypt(String input)
{
int16_t firstKey = 0xab;
String buf;
int key;
int i;
buf = input;
key = firstKey;
i = 0;
for (;i < buf.length();(i = i + 1))
{
buf[i] ^= key;
key = buf[i];
}
return buf;
}
// This does not function yet, as I'm pretty lost..
// This was orginally converted from typescript with https://andrei-markeev.github.io/ts2c/
// I started work on converting this, but ran into errors I don't know how to solve.
String encryptWithHeader(String input){
String msgBuf;
String outBuf;
int16_t firstKey = 0xab;
char * null = NULL;
msgBuf = encrypt(input);
outBuf = msgBuf.length() +1;
//this is where I got lost...
assert(null != NULL);
null[0] = '\0';
strncat(null, outBuf, msgBuf.length());
str_int16_t_cat(null, 4);
outBuf = msgBuf + 4
return outBuf;
}
Finally, the data:
//this is the unencrypted json
String offMsg = "{\"system\":{\"set_relay_state\":{\"state\":0}}}";
//current encrypt function produces:
d0f281f88bff9af7d5ef94b6c5a0d48bf99cf091e8b7c4b0d1a5c0e2d8a381f286e793f6d4eedea3dea3
//the working "withheaders" should produce:
00002ad0f281f88bff9af7d5ef94b6c5a0d48bf99cf091e8b7c4b0d1a5c0e2d8a381f286e793f6d4eedea3dea3
Admittedly my C/C++ ability is very limited and I can spell typescript, that's about all. I have a very extensive history with PHP. As useful as that is. So, I understand the basics of data structures and whatnot, but I'm venturing off into areas I've never been in. Any help would be greatly appreciated.
It looks like the encryption is fairly simple: write the current character XORed with the key to the buffer and make that newly written character the new key. It also looks like the "withHeaders" version adds the length of the encrypted string as a 4 byte integer to the start of the buffer. I think it might be easier to allocate a character array and pass that array to a function that writes the result to that buffer. For example:
void encryptWithHeader(byte buffer[], int bufferLength, byte key, String message) {
int i;
uint32_t messageLength = message.length();
Serial.println(message);
Serial.println(message.length());
// check that we won't overrun the buffer
if ( messageLength + 5 < bufferLength) {
buffer[0] = messageLength >> 24 & 0xFF;
buffer[1] = messageLength >> 16 & 0xFF;
buffer[2] = messageLength >> 8 & 0xFF;
buffer[3] = messageLength & 0xFF;
for (i = 0; i < messageLength; i++) {
buffer[i + 4] = message[i] ^ key;
key = buffer[i + 4];
}
}
else { // we would have overrun the buffer
Serial.println("not enough room in buffer for message");
}
}
void setup() {
// put your setup code here, to run once:
Serial.begin(9600);
}
void loop() {
byte theBuffer[64];
int i;
String offMsg = "{\"system\":{\"set_relay_state\":{\"state\":0}}}";
encryptWithHeader(theBuffer, 64, 0xab, offMsg);
// now print it out to check
for (i = 0; i < offMsg.length() + 4; i++) {
if (theBuffer[i] < 0x10) // adds an extra zero if a byte prints as on1y 1 char
Serial.print("0");
Serial.print(theBuffer[i], HEX);
}
while (true)
;
}
If you want to send the character buffer to a remote device you can send it out one byte at a time:
for (i = 0; i < offMsg.length() + 4; i++)
Serial.write(theBuffer[i]);

Reading JSON from Serial port missing part of the starting data

When reading a JSON string from the serial port on an ESP8266 it cuts off the beginning of the data.
I have tried reading data from the Serial port and printing each character, however it is cutting off part of the begging of the data.
void setup() {
Serial.begin(115200);
while (!Serial) {
;
}
}
void loop() {
int curSize = 30;
char* buffer = new char[curSize];
std::fill_n(buffer, curSize, 0);
int pos = 0;
Serial.print("Sending: ");
while(Serial.available() == false) delay(500);
while (Serial.available()) {
char c = Serial.read();
Serial.print(c);
if(pos == curSize-1){
char* newBuffer = increaseBuffer(buffer, curSize, curSize + 30);
curSize += 30;
delete[] buffer;
buffer = newBuffer;
}
if(c == '\n'){
buffer[pos] = 0;
pos = 0;
break;
}
buffer[pos++] = c;
}
if(buffer[0] != 0) {
sendBuffer(buffer);
}
delete[] buffer;
}
char* increaseBuffer(char* orig, int oldSize, int newSize){
char* data = new char[newSize];
std::fill_n(data, newSize, 0);
for(int i = 0; i < newSize; i++){
if(i < oldSize) data[i] = orig[i];
else data[i] = '\0';
}
return data;
}
JSON data used (and expected output)
{"type":0,"ver":"0.0.1","T":[28,29,29,29,29,29,29,29,29,29],"H":[59.1608,59.1608,60,59.1608,60,60,60,59.1608,59.1608,59.1608],"DP":[20.36254,20.36254,20.59363,20.36254,20.59363,20.59363,20.59363,20.36254,20.36254],"HI":[30.90588,30.90588,31.0335,30.90588,31.0335,31.0335,31.0335,30.90588,30.90588]}
examples of what is actually output
Example 1: 9,29,29,29,29,29,29,29,29],"H":[59.1608,59.1608,60,59.1608,60,60,60,59.1608,59.1608,59.1608],"DP":[20.36254,20.36254,20.59363,20.36254,20.59363,20.59363,20.59363,20.36254,20.36254],"HI":[30.90588,30.90588,31.0335,30.90588,31.0335,31.0335,31.0335,30.90588,30.90588]}
Example 2: 29,29,29,29,29,29,29,29,29],"H":[59.1608,59.1608,60,59.1608,60,60,60,59.1608,59.1608,59.1608],"DP":[20.36254,20.36254,20.59363,20.36254,20.59363,20.59363,20.59363,20.36254,20.36254],"HI":[30.90588,30.90588,31.0335,30.90588,31.0335,31.0335,31.0335,30.90588,30.90588]}
Try making the delay 1 instead of 500 in the blocking loop that's waiting for data to start coming in. I'm going to guess what happens is that on one iteration of that loop Serial.available() is false and during the delay you start to get data coming in that ends up getting written over by the time your delay ends to check again.
What I'm picturing is the following. If you were to expand out that delay(500) to be delay(1) called 500 times.
while(Serial.available() == false){
delay(1);
delay(1);
// ...
delay(1); // first character comes in
delay(1);
delay(1); // second character comes in
// ...
delay(1); // n character comes in
}
Then after the delay is over you start actually collecting the characters that are coming in.

C, How to save String into binary file?

I want to save String array to binary file.
I searched and tried, didn't come up with normal binary file contents.
(not like 00 0A 10) came up with (8죅? <죅? A죅? E죅?) - written in korean.
the writing part of the code was
FILE *fp_course = fopen("KJH_course.dat", "wb");
while ((row = mysql_fetch_row(res)) != NULL) { //JUST FOR FETCHING DATA FROM MYSQL, WORKED WELL
printf("%s %s %s %s\n", row[0], row[1], row[2], row[3]);
fwrite(row, sizeof(row), 1, fp_course);
};
the data in row is {'32141183', 'JHK', '2'}
(the last row[3] was used for larger array later)
did i wrote something wrong?
The MySQL documentation has an example:
MYSQL_ROW row;
unsigned int num_fields;
unsigned int i;
num_fields = mysql_num_fields(result);
while ((row = mysql_fetch_row(result)))
{
unsigned long *lengths;
lengths = mysql_fetch_lengths(result);
for(i = 0; i < num_fields; i++)
{
printf("[%.*s] ", (int) lengths[i],
row[i] ? row[i] : "NULL");
}
printf("\n");
}
Note that MYSQL_ROW returned does not necessarily have nul terminated strings (as per a comment, quoted below), so you can't safely call strlen() on the columns - instead you have to call mysql_fetch_lengths().
MYSQL_ROW is an array of null-terminated strings. (However, you cannot treat these as null-terminated strings if field values may contain binary data, because such values may contain null bytes internally.)
The sizeof operator does not return the length of a string, I suppose that row is a pointer, sizeof it is simply the size of a pointer, you can't do what you want the way you tried.
You need the length of each item, so
for (int i = 0; i < 4 ; ++i) {
fwrite(row[i], 1, strlen(row[i]), fp_course);
}
VERY IMPORTANT NOTE
The contents written to the file this way, are useless, because you don't know the length of each string so there is no way to read them back.
So perhaps you are required to do something like
for (int i = 0; i < 4 ; ++i) {
char *string;
int length;
string = row[i];
length = strlen(string);
fwrite(&length, sizeof(length), 1, fp_course);
fwrite(string, 1, length, fp_course);
}
this way you can read the data back.

how to return a structure (defined in .h file) from a function in another cpp file?

I ran into this issue and I cannot handle it. Any suggestion is appreciated.
I have a structure defined in a header file as follows:
Results.h
#ifndef RESULTS_H
#define RESULTS_H
struct Results
{
double dOptSizeMWh;
double dOrigSOCFinal;
double dManiSOCFinal;
};
#endif
and a general definition of "Deterministic" function in Deterministic.h:
#ifndef DETERMINISTIC_H
#define DETERMINISTIC_H
Results Deterministic(int,int,int,double,double); //Deterministic(int nNoMonth, int nNOWind, int nWindLength, double dPreviousSizeMWh, double dPreviousSOC)
#endif;
This function is implemented in Deterministic.cpp:
#include "Results.h"
Results Deterministic(int nNoMonth, int nNOWind, int nWindLength, double dPreviousSizeMWh, double dPreviousSOC)
{
// returns number of rows and columns of the array created
struct Results sRes;
sRes.dOptSizeMWh = -1.0; // for the optimal size of battery in MWh
sRes.dOrigSOCFinal = -1.0; // for the SOC at the end of the window
sRes.dManiSOCFinal = -1.0; // this is set to 0.0 if final SOC is slightly below 0
//...........................////
// OTHER Calculation .......////
//...........................////
return sRes;
}
Finally, I have a main file which I call Deterministic function and I use Results structure, main.cpp:
#include <Results.h>
#include <Deterministic.h>
using namespace std;
int main ()
{
int nNoMonth = 1; // the month that we want to use in the input
int nWindLength = 1; // length of window, hour
int nNODays = 1; // number of days that we want to repeat optimization
struct Results dValues;
double **mRes = new double*[nNODays * 24 / nWindLength];
for (int i = 0; i < nNODays * 24 / nWindLength; ++i) mRes[i] = new double[3];
for (int i = 0; i < nNODays * 24 / nWindLength; i++)
{
if (i == 0)
{
dValues = Deterministic(nNoMonth, i, nWindLength, 0.0, 0.0);
}else
{
temp0 = *(*(mRes+i-1)); double temp1 = *(*(mRes+i-1)+1); double temp2 = *(*(mRes+i-1)+2);
if (temp2 == -1.0) {dValues = Deterministic(nNoMonth, i, nWindLength, temp0, temp1);}
else {dValues = Deterministic(nNoMonth, i, nWindLength, *(*(mRes+i-1)), *(*(mRes+i-1)));}
}
*(*(mRes+i)) = dValues.dOptSizeMWh;
*(*(mRes+i)+1) = dValues.dOrigSOCFinal;
*(*(mRes+i)+2) = dValues.dManiSOCFinal;
}
these are only a small portion of the codes in Deterministic.cpp and main.cpp which defines the problem. First loop goes well (i.e., i=0) without any problem, but it fails in the second loop and beyond with this error: "R6010 - abort() has been called"
This error comes up in the main.cpp where I call Deterministic function in the if statement.
I have no problem compiling and running the posted code (other than the missing double in front of the declaration of temp). Without knowing what Deterministic() is actually doing, it's a bit hard to guess what the problem is (divide by zero? playing a Justin Bieber mp3?). It shouldn't have anything to do with returning a structure from a function defined in another file (translation units are a fundamental feature of the language). To find the root cause, single-step through the (complete) Deterministic() using your debugger.

CUDA memory troubles

I have a CUDA kernel which I'm compiling to a cubin file without any special flags:
nvcc text.cu -cubin
It compiles, though with this message:
Advisory: Cannot tell what pointer points to, assuming global memory space
and a reference to a line in some temporary cpp file. I can get this to work by commenting out some seemingly arbitrary code which makes no sense to me.
The kernel is as follows:
__global__ void string_search(char** texts, int* lengths, char* symbol, int* matches, int symbolLength)
{
int localMatches = 0;
int blockId = blockIdx.x + blockIdx.y * gridDim.x;
int threadId = threadIdx.x + threadIdx.y * blockDim.x;
int blockThreads = blockDim.x * blockDim.y;
__shared__ int localMatchCounts[32];
bool breaking = false;
for(int i = 0; i < (lengths[blockId] - (symbolLength - 1)); i += blockThreads)
{
if(texts[blockId][i] == symbol[0])
{
for(int j = 1; j < symbolLength; j++)
{
if(texts[blockId][i + j] != symbol[j])
{
breaking = true;
break;
}
}
if (breaking) continue;
localMatches++;
}
}
localMatchCounts[threadId] = localMatches;
__syncthreads();
if(threadId == 0)
{
int sum = 0;
for(int i = 0; i < 32; i++)
{
sum += localMatchCounts[i];
}
matches[blockId] = sum;
}
}
If I replace the line
localMatchCounts[threadId] = localMatches;
after the first for loop with this line
localMatchCounts[threadId] = 5;
it compiles with no notices. This can also be achieved by commenting out seemingly random parts of the loop above the line. I have also tried replacing the local memory array with a normal array to no effect. Can anyone tell me what the problem is?
The system is Vista 64bit, for what its worth.
Edit: I fixed the code so it actually works, though it still produces the compiler notice. It does not seem as though the warning is a problem, at least with regards to correctness (it might affect performance).
Arrays of pointers like char** are problematic in kernels, since the kernels have no access to the host's memory.
It is better to allocate a single continuous buffer and to divide it in a manner that enables parallel access.
In this case I'd define a 1D array which contains all the strings positioned one after another and another 1D array, sized 2*numberOfStrings which contains the offset of each string within the first array and it's length:
For example - preparation for kernel:
char* buffer = st[0] + st[1] + st[2] + ....;
int* metadata = new int[numberOfStrings * 2];
int lastpos = 0;
for (int cnt = 0; cnt < 2* numberOfStrings; cnt+=2)
{
metadata[cnt] = lastpos;
lastpos += length(st[cnt]);
metadata[cnt] = length(st[cnt]);
}
In kernel:
currentIndex = threadId + blockId * numberOfBlocks;
char* currentString = buffer + metadata[2 * currentIndex];
int currentStringLength = metadata[2 * currentIndex + 1];
The problem seems to be associated with the char** parameter. Turning this into a char* solved the warning, so I suspect that cuda might have problems with this form of data. Perhaps cuda prefers that one uses the specific cuda 2D arrays in this case.