Optimize this function? [closed] - c++

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
This question does not appear to be about programming within the scope defined in the help center.
Closed 8 years ago.
Improve this question
I have written this code (Uses V8 library). I went through it a couple of times and this feels like it's the only way I can write something like this. The aim of the function is to replace the JavaScript .split() function; as when using that function with a limit doesn't include the last part of the array in the returning array. EG:
var str = "Test split string with limit";
var out = str.split(' ', 2);
The array out will contain: [Test, split]. I want it to contain: [Test, split, string with limit].
I know there are pure JS ways to do this however I find them hacky and possibly slower(?) than a single C++ bind call.
Here's my function:
/**
* Explodes a string but limits the tokens
* #param input
* #param delim
* #param limit
* #return
*/
void ASEngine::Engine::ASstrtok(const v8::FunctionCallbackInfo<v8::Value>& args)
{
Assert(3, args);
Isolate* isolate = args.GetIsolate();
/* Get args */
String::Utf8Value a1(args[0]);
String::Utf8Value a2(args[1]);
Local<Uint32> a3 = args[2]->ToUint32();
std::string input = std::string(*a1);
std::string delim = std::string(*a2);
unsigned int limit = a3->Int32Value();
unsigned int inputLen = input.length();
// Declare a temporary array to shove into the return later
std::vector<char*> tmp;
tmp.reserve(limit);
unsigned int delimlen = delim.length();
char* cp = (char*) malloc(inputLen);
char* cursor = cp + inputLen; // Cursor
char* cpp = (char*) cp; // Keep the start of the string
// Copy the haystack into a modifyable char ptr
memset(cp + inputLen, 0x00, 1);
memcpy(cp, input.c_str(), inputLen);
unsigned int arrayIndex = 0;
for(unsigned int i=0;i<limit;i++)
{
if((cursor = strstr(cp, delim.c_str())) == NULL)
{
cursor = (char*) cpp + inputLen;
break;
}
for(int j=0;j<delimlen;j++)
*(cursor+j) = 0x00;
tmp.push_back(cp);
cp = cursor + delimlen;
arrayIndex++;
}
if(*(cp) != '\0')
{
arrayIndex++;
tmp.push_back(cp);
}
Handle<Array> rtn = Array::New(args.GetIsolate(), arrayIndex);
/* Loop through the temporary array and assign
the variables to the V8 array */
for(unsigned int i=0;i<arrayIndex;i++)
{
rtn->Set(i, String::NewFromUtf8(
isolate, tmp[i], String::kNormalString, strlen(tmp[i])
));
}
/* Clean up memory */
delete cpp;
cp = NULL;
cpp = NULL;
cursor = NULL;
isolate = NULL;
/* Set the return */
args.GetReturnValue().Set(rtn);
}
If you are wondering: The variable cpp is there so I can delete the character pointer after I am done (As calling v8's String::NewFromUtf8() function copies the string) and I modify the cp pointer during the process of the function.

Before optimising, I would fix the code so that it is correct.
char* cp = (char*) malloc(inputLen);
...
/* Clean up memory */
delete cpp;
Whilst in some implementations, new and malloc do exactly the same thing, other implementations do not. So, if you allocate with malloc, use free to free the memory, not delete.
If you want to be clever about it, I expect:
tmp.reserve(limit+1);
will ensure that you have space for the remainder of the string without further allocation in the vector.
Since cursor isn't used after the loop, setting it inside the if that breaks the loop makes no sense.
if((cursor = strstr(cp, delim.c_str())) == NULL)
{
cursor = (char*) cpp + inputLen;
break;
}
You are using casts to (char *) in places that don't need it, for example:
char* cpp = (char*) cp; // Keep the start of the string
(cp is a char * already).
This:
memset(cp + inputLen, 0x00, 1);
is the same as:
cp[inputlen] = 0;
but unless the compiler inlines the memset, much faster.
Likewsie:
*(cursor+j) = 0x00;
can be written:
cursor[j] = 0;
However, assuming delimLen is greater than 1, you could get away with:
for(int j=0;j<delimlen;j++)
*(cursor+j) = 0x00;
converted to:
*cursor = 0;
Since your new cp value will skip to beyond delimlen anyway.
These serve absolutely no purpose:
cp = NULL;
cpp = NULL;
cursor = NULL;
isolate = NULL;
Unfortunately, I expect most of the time in your function won't be in any of the code I've commented on. But in the passing arguments back and forth between the calling JS library and the native C++ code. I'd be surprised if you gain anything over writing the same code in JS. (None of the above make much of a difference when it comes to speed, it's just correctness and "a small number of potentially wasted cycles if the compiler is rather daft").

Related

Adding filenames from a directory to a char* array. c++

I ame trying to get filenames from a directory and put it in a char* array for latter use. But this dont seem to work the way i want to. When printing it only showes the last filename on all spots.
So my question howe can i add the file names in every spot inside the char*[]?
/*Placed outside*/
int i = 0;
char* Files[20] = {};
/*Placed outside*/
while (handle != INVALID_HANDLE_VALUE)
{
char buffer[4100];
sprintf_s(buffer, "%ls", search_data.cFileName);
Files[i] = buffer;
i++;
if (FindNextFile(handle, &search_data) == FALSE)
/*Printing I use ImGui*/
#define IM_ARRAYSIZE(_ARR) ((int)(sizeof(_ARR)/sizeof(*_ARR)))
static int listbox_item_current = 1;
ImGui::ListBox("", &listbox_item_current, Files, i, 4);
You could use C++ standard filesystem, but for that I guess you would need C++17 (or atleast VS15), not really sure.
You would have to include:
#include <experimental/filesystem>
#include <filesystem>
using namespace std::experimental::filesystem::v1;
Using it should be simple:
int i = 0;
const char * directoryToSearch = "C:\etc\etc";
for (const auto & file : directory_iterator(directoryToSearch)) {
files[i] = new char[file.path().stem().string().length() + 1];
strcpy(files[i], file.path().stem().string().c_str());
++i;
}
Indeed, you should clean up the array after you're done using it. Don't forget, not many compilers support this at the moment.
When printing it only shows the last filename on all spots. That is just normal: you store the filename on each iteration in the same buffer and just copy the address of the buffer into your array. Unrelated to the question, as buffer is an automatic variable declared inside a loop (block scoped), using it outside of the loop is Undefined Behaviour, so you end with an array of dangling pointers.
The correct way would be to either use a 2D-array char Files[MAX_PATH][20]; and store a file name in each slot, or use dynamic memory allocate by new (or malloc at a lower level). For the second option, you can do it by hand, allocating memory for each file name - and remember to free anything at the end, or you can let the standard library manage it for you by using:
std::vector<std::string> Files;
...
while(...) {
...
File.push_back(search_data.cFileName);
Dear ImGui provides a ListBoxctor that allows to pass an opaque data storage along with an extractor, and it can be used here:
bool string_vector_items_getter(void* data, int idx, const char** out_text) {
std::vector<std::string> *v = reinterpret_cast<std::vector<std::string> >(data);
if (idx < 0 || idx >= v.size()) return false;
*out_text = v[idx].c_str();
return true;
}
and then:
ImGui::ListBox("", &listbox_item_current, &string_vector_items_getter, &Files, i, 4);
(beware: untested code!)

An issue with memory allocations of arrays of the same size

I'm having a weird behaviour with my C++ code. Here it is.
OI_Id * Reqlist = 0;
int * Idlist = 0;
int Reqsize = listcount; // we calculate listcount somehow earlier.
Idlist = new int [Reqsize];
if (Idlist == 0)
{
return;
}
printf ("Idlist = %0x",Idlist);
Reqlist = new OI_Id [Reqsize]; // OI_Id is a 3rd party lib simple struct.
if (Reqlist == 0)
{
return;
}
printf ("Reqlist = %0x",Reqlist);
So the problem is that in both cases it prints the same value - the same pointer is returned by the new operator. BUT! If we change the length of second allocated array to another value (Reqsize+ 1, for example), everything is OK.
Did anybody meet any similar behaviour? I have no idea what's the reason of the problem.

Find longest Valid Parentheses(leetcode) [closed]

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
Questions asking for code must demonstrate a minimal understanding of the problem being solved. Include attempted solutions, why they didn't work, and the expected results. See also: Stack Overflow question checklist
Closed 9 years ago.
Improve this question
eg: if a string is ")(((())))))(", the answer should be 8.
I implement as follow:(learn from others)
int longestValidParentheses(string s) {
int len = s.length();
char* str = new char[len+1];
strcpy(str,s.c_str());
int maxlen=0;
stack<char*> stk;
char* cur = str;
while(*cur)
{
if(*cur=='(')
stk.push(cur);
else
{
if(!stk.empty() && *stk.top()=='(')
{
stk.pop();
maxlen = max(maxlen, cur-(stk.empty()?str-1:stk.top()));
}
else
stk.push(cur);
}
cur++;
}
return maxlen;
}
but I don't know why for this code, char* str = new char[len+1] but not char* str = new char[len]. Could anybody tell me why? If I use char* str = new char[len], an error will occur.
As you can tell from the comments, your problem is in the fact that you are mixing some constructs that are really C++ (like the string type) with functions and data types that are more typical of C (like char*, strcpy, etc). Bottom line: the length of a string is the number of characters in it; but the storage space needed is bigger than that, because a char* type of string has a terminating '\0' character to indicate "end of string". If you don't allocate space for that, then the '\0' that is appended at the end of the string copy operation will overwrite another memory location with possibly disastrous consequences.
You have no need to make a copy of the string in order to iterate it, since string comes with perfectly nice iterators that work exactly like char*:
The first 10 or so lines can be replaced by:
int maxlen = 0;
auto cur = s.begin(), end = s.end();
std::stack<decltype(cur)> stk;
while (cur != end) {
If you don't have C++11, use
std::string::iterator
in place of auto and decltype(cur).
One can also use indexes, or even make a char* into the string's own content:
cur = &s[0];
In no case is making a copy of the string and then leaking it a good idea.
int longestValidParentheses(string s){
int pos = 0;
int maxlen = 0;
while(pos != -1){
pos = substr(pos).find("(");
if(str.substr(pos).find(")") == -1) pos = -1;
maxlen += 2;
pos++;
}
return maxlen;
}
If you're using C++ might as well use the tools you have been given.
Floris already had given you the answer for your question. Let me just add one thing: you did not have to make a copy of that string. Note that you are allocating a buffer (new [len+1]) and you are strcpy'ing into it a value that you've got from s.c_str().
Check what is the c_str! The thing you are copying into your buffer is just an array of chars, char*, that you can completely appropriately use just like your new buffer. Try it:
int len = s.length();
char const* str = s.c_str();
and it should work just the same, without having to make a copy. (maybe you'll need to add some consts in a few places)

array assignment access violation

I’ve read many Q&A's which seemed similar to this problem but haven’t found any answers yet:
I have to make some assignments to a dynamic byte array in the fillbyte function like this:
int Error;
result = fillbyte (&Error);
if I comment the line shown below, everything works fine. but if that line gets executed, the second time that this function is called, access violation exception will be raised however the first time the code runs properly and everything goes alright. I can’t seem to find the problem with this line of code or another way to fill the array with password bytes.
Bool fillbyte(int *Error)
{
byte BCC;
byte *Packet1 = new byte;
*Packet1 = 0x01;
*(Packet1+1) = 'P';
*(Packet1+2) = '1';
*(Packet1+3) = STX;
*(Packet1+4) = '(';
int add = sizeof(readingprops.password)*2;
for(int i=0;i<add;i++)
{
*(Packet1+(5+i)) = readingprops.password[i]; //this line raises the problem
}
*(Packet1+add+5) = ')';
*(Packet1+add+6) = ETX;
BCC = calc.CalcBCC(Packet1,add+7);
*(Packet1+add+7) = BCC;
SerialPort.Write(Packet1,add+8);
delete Packet1;
return true;
}
Any help would be appreciated
I don't see how it can ever work. You allocate one byte on the heap but treat it as multiple bytes:
byte *Packet1 = new byte;
*Packet1 = 0x01;
*(Packet1+1) = 'P'; // !!!
*(Packet1+2) = '1'; // !!!
*(Packet1+3) = STX; // !!!
*(Packet1+4) = '('; // !!!
Here you allocate just one byte
byte *Packet1 = new byte;
and then use the pointer beyond the allocated memory
*(Packet1+1) = 'P';
*(Packet1+2) = '1';
*(Packet1+3) = STX;
*(Packet1+4) = '(';
This causes undefined behaviour, sometimes it may work. So you want something like
byte Packet1 = new byte[size]
where size is appropriate for your needs (probably add + 8, since this is the amount of bytes you write to in that function). Then delete it with delete[]. You could also use stack allocation, or std::vector<byte> since this is c++.

C code - need to clarify the effectiveness

Hi I have written a code based upon a requirement.
(field1_6)(field2_30)(field3_16)(field4_16)(field5_1)(field6_6)(field7_2)(field8_1).....
this is one bucket(8 fields) of data. we will receive 20 buckets at a time means totally 160 fields.
i need to take the values of field3,field7 & fields8 based upon predefined condition.
if teh input argument is N then take the three fields from 1st bucket and if it is Y i need
to take the three fields from any other bucket other than 1st one.
if argumnet is Y then i need to scan all the 20 buckets one after other and check
the first field of the bucket is not equal to 0 and if it is true then fetch the three fields of that bucket and exit.
i have written the code and its also working fine ..but not so confident that it is effctive.
i am afraid of a crash some time.please suggest below is the code.
int CMI9_auxc_parse_balance_info(char *i_balance_info,char *i_use_balance_ind,char *o_balance,char *o_balance_change,char *o_balance_sign
)
{
char *pch = NULL;
char *balance_id[MAX_BUCKETS] = {NULL};
char balance_info[BALANCE_INFO_FIELD_MAX_LENTH] = {0};
char *str[160] = {NULL};
int i=0,j=0,b_id=0,b_ind=0,bc_ind=0,bs_ind=0,rc;
int total_bukets ;
memset(balance_info,' ',BALANCE_INFO_FIELD_MAX_LENTH);
memcpy(balance_info,i_balance_info,BALANCE_INFO_FIELD_MAX_LENTH);
//balance_info[BALANCE_INFO_FIELD_MAX_LENTH]='\0';
pch = strtok (balance_info,"*");
while (pch != NULL && i < 160)
{
str[i]=(char*)malloc(strlen(pch) + 1);
strcpy(str[i],pch);
pch = strtok (NULL, "*");
i++;
}
total_bukets = i/8 ;
for (j=0;str[b_id]!=NULL,j<total_bukets;j++)
{
balance_id[j]=str[b_id];
b_id=b_id+8;
}
if (!memcmp(i_use_balance_ind,"Y",1))
{
if (atoi(balance_id[0])==1)
{
memcpy(o_balance,str[2],16);
memcpy(o_balance_change,str[3],16);
memcpy(o_balance_sign,str[7],1);
for(i=0;i<160;i++)
free(str[i]);
return 1;
}
else
{
for(i=0;i<160;i++)
free(str[i]);
return 0;
}
}
else if (!memcmp(i_use_balance_ind,"N",1))
{
for (j=1;balance_id[j]!=NULL,j<MAX_BUCKETS;j++)
{
b_ind=(j*8)+2;
bc_ind=(j*8)+3;
bs_ind=(j*8)+7;
if (atoi(balance_id[j])!=1 && atoi( str[bc_ind] )!=0)
{
memcpy(o_balance,str[b_ind],16);
memcpy(o_balance_change,str[bc_ind],16);
memcpy(o_balance_sign,str[bs_ind],1);
for(i=0;i<160;i++)
free(str[i]);
return 1;
}
}
for(i=0;i<160;i++)
free(str[i]);
return 0;
}
for(i=0;i<160;i++)
free(str[i]);
return 0;
}
My feeling is that this code is very brittle. It may well work when given good input (I don't propose to desk check the thing for you) but if given some incorrect inputs it will either crash and burn or give misleading results.
Have you tested for unexpected inputs? For example:
Suppose i_balance_info is null?
Suppose i_balance_info is ""?
Suppose there are fewer than 8 items in the input string, what will this line of code do?
memcpy(o_balance_sign,str[7],1);
Suppose that that the item in str[3] is less than 16 chars long, what will this line of code do?
memcpy(o_balance_change,str[3],16);
My approach to writing such code would be to protect against all such eventualities. At the very least I would add ASSERT() statements, I would usually write explicit input validation and return errors when it's bad. The problem here is that the interface does not seem to allow for any possibility that there might be bad input.
I had a hard time reading your code but FWIW I've added some comments, HTH:
// do shorter functions, long functions are harder to follow and make errors harder to spot
// document all your variables, at the very least your function parameters
// also what the function is suppose to do and what it expects as input
int CMI9_auxc_parse_balance_info
(
char *i_balance_info,
char *i_use_balance_ind,
char *o_balance,
char *o_balance_change,
char *o_balance_sign
)
{
char *balance_id[MAX_BUCKETS] = {NULL};
char balance_info[BALANCE_INFO_FIELD_MAX_LENTH] = {0};
char *str[160] = {NULL};
int i=0,j=0,b_id=0,b_ind=0,bc_ind=0,bs_ind=0,rc;
int total_bukets=0; // good practice to initialize all variables
//
// check for null pointers in your arguments, and do sanity checks for any
// calculations
// also move variable declarations to just before they are needed
//
memset(balance_info,' ',BALANCE_INFO_FIELD_MAX_LENTH);
memcpy(balance_info,i_balance_info,BALANCE_INFO_FIELD_MAX_LENTH);
//balance_info[BALANCE_INFO_FIELD_MAX_LENTH]='\0'; // should be BALANCE_INFO_FIELD_MAX_LENTH-1
char *pch = strtok (balance_info,"*"); // this will potentially crash since no ending \0
while (pch != NULL && i < 160)
{
str[i]=(char*)malloc(strlen(pch) + 1);
strcpy(str[i],pch);
pch = strtok (NULL, "*");
i++;
}
total_bukets = i/8 ;
// you have declared char*str[160] check if enough b_id < 160
// asserts are helpful if nothing else assert( b_id < 160 );
for (j=0;str[b_id]!=NULL,j<total_bukets;j++)
{
balance_id[j]=str[b_id];
b_id=b_id+8;
}
// don't use memcmp, if ('y'==i_use_balance_ind[0]) is better
if (!memcmp(i_use_balance_ind,"Y",1))
{
// atoi needs balance_id str to end with \0 has it?
if (atoi(balance_id[0])==1)
{
// length assumptions and memcpy when its only one byte
memcpy(o_balance,str[2],16);
memcpy(o_balance_change,str[3],16);
memcpy(o_balance_sign,str[7],1);
for(i=0;i<160;i++)
free(str[i]);
return 1;
}
else
{
for(i=0;i<160;i++)
free(str[i]);
return 0;
}
}
// if ('N'==i_use_balance_ind[0])
else if (!memcmp(i_use_balance_ind,"N",1))
{
// here I get a headache, this looks just at first glance risky.
for (j=1;balance_id[j]!=NULL,j<MAX_BUCKETS;j++)
{
b_ind=(j*8)+2;
bc_ind=(j*8)+3;
bs_ind=(j*8)+7;
if (atoi(balance_id[j])!=1 && atoi( str[bc_ind] )!=0)
{
// length assumptions and memcpy when its only one byte
// here u assume strlen(str[b_ind])>15 including \0
memcpy(o_balance,str[b_ind],16);
// here u assume strlen(str[bc_ind])>15 including \0
memcpy(o_balance_change,str[bc_ind],16);
// here, besides length assumption you could use a simple assignment
// since its one byte
memcpy(o_balance_sign,str[bs_ind],1);
// a common practice is to set pointers that are freed to NULL.
// maybe not necessary here since u return
for(i=0;i<160;i++)
free(str[i]);
return 1;
}
}
// suggestion do one function that frees your pointers to avoid dupl
for(i=0;i<160;i++)
free(str[i]);
return 0;
}
for(i=0;i<160;i++)
free(str[i]);
return 0;
}
A helpful technique when you want to access offsets in an array is to create a struct that maps the memory layout. Then you cast your pointer to a pointer of the struct and use the struct members to extract information instead of your various memcpy's
I would also suggest you reconsider your parameters to the function in general, if you place every of them in a struct you have better control and makes the function more readable e.g.
int foo( input* inbalance, output* outbalance )
(or whatever it is you are trying to do)