Register a SA Forums Account here!
JOINING THE SA FORUMS WILL REMOVE THIS BIG AD, THE ANNOYING UNDERLINED ADS, AND STUPID INTERSTITIAL ADS!!!

You can: log in, read the tech support FAQ, or request your lost password. This dumb message (and those ads) will appear on every screen until you register! Get rid of this crap by registering your own SA Forums Account and joining roughly 150,000 Goons, for the one-time price of $9.95! We charge money because it costs us money per month for bills, and since we don't believe in showing ads to our users, we try to make the money back through forum registrations.
 
  • Post
  • Reply
sarehu
Apr 20, 2007

(call/cc call/cc)

Presto posted:

Not much because you really want sizeof(*s). :)

Oh.

Okay then...

:smithicide:

Adbot
ADBOT LOVES YOU

hooah
Feb 6, 2006
WTF?
I ended up copying the strings into the argument array for execvp on my own since the suggested functions caused various problems. I was successful once in not having the call to execvp fail, but I got some permissions error (I believe from the system). From then on out, however, I've been having very strange behavior with the argument array. Here's the function; the command being read in is "ls -l", with the command's parameters array holding "ls" and "-l":
C++ code:
void ExternalCmd::executeCmd()
{
    //size_t i = 0;
    //size_t j = 0;
    char *argv[10];
	for(size_t i = 0; i < MAXPARAM; i++)
    {
        char intermediate[10];
        cout << "parameter " << i << "'s length: " << parameters[i].length() << endl;
        for(size_t j = 0; j < parameters[i].length(); j++)
        {
            intermediate[j] = parameters[i].at(j);
        }
        //intermediate[j] = '\0';
        argv[i] = intermediate;
    }
    execvp(argv[0], argv);
    cout << "exec failed" << endl;
}
When I step through with the debugger, at the end of the i = 0 loop, I see argv[0] change to ls like I would expect. Then, as soon as the "-" is read in, it replaces the first element in argv[0], so argv[0] looks like "-s". Why in the gently caress would that be happening? It makes no sense to me!

roomforthetuna
Mar 22, 2005

I don't need to know anything about virii! My CUSTOM PROGRAM keeps me protected! It's not like they'll try to come in through the Internet or something!

hooah posted:

When I step through with the debugger, at the end of the i = 0 loop, I see argv[0] change to ls like I would expect. Then, as soon as the "-" is read in, it replaces the first element in argv[0], so argv[0] looks like "-s". Why in the gently caress would that be happening? It makes no sense to me!
You're setting argv[0] (a pointer to chars) to the address of the first element of "intermediate", which promptly goes out of scope, essentially rendering argv[0] invalid and unpredictable. Then in the next iteration of the loop, a new "intermediate" is put on the stack, conveniently in the same place as the previous one, which is also the address argv[0] is pointing at. Then you write something to it, and there you go, argv[0] is now pointing at different data.

If you want the data to persist long enough for exec you can do one of:

1. malloc space on the heap for each argument, and need to clean it up after calling exec.

2. have a 2D space for your intermediate arguments, so each is stored in a different space, and don't let that space go out of scope until after you've called exec.
ie.
char intermediate[10][10] containing
[l][s][0][7 uninitialized values]
[-][l][0][7 uninitialized values]
[10 uninitialized values]
etc.
argv[0] = intermediate[0]
argv[1] = intermediate[1]

3. have a longer array of chars for your intermediate arguments and stick them one after another separated by null-terminators, and make the argv pointers point at the appropriate position in your single array. Don't let that space go out of scope until after you've called exec either.
ie.
char intermediate[100] containing
[l][s][0][-][l][0][94 uninitialized values]

argv[0] = &intermediate[0]
argv[1] = &intermediate[3]

With either of 2 or 3 you want to validate that the size of your parameters isn't going to overflow your storage space. 1 has its own disadvantages.

(Note, for easier readability 0 represents \0 the null terminator in the arrays above, not the character 0.)

roomforthetuna fucked around with this message at 15:11 on Sep 19, 2014

hooah
Feb 6, 2006
WTF?

roomforthetuna posted:

You're setting argv[0] (a pointer to chars) to the address of the first element of "intermediate", which promptly goes out of scope, essentially rendering argv[0] invalid and unpredictable. Then in the next iteration of the loop, a new "intermediate" is put on the stack, conveniently in the same place as the previous one, which is also the address argv[0] is pointing at. Then you write something to it, and there you go, argv[0] is now pointing at different data.

Ok, then how do I set argv[0] to the contents of intermediate, rather than its address?

roomforthetuna
Mar 22, 2005

I don't need to know anything about virii! My CUSTOM PROGRAM keeps me protected! It's not like they'll try to come in through the Internet or something!

hooah posted:

Ok, then how do I set argv[0] to the contents of intermediate, rather than its address?
Practical answer above in a simultaneous edit, but the real answer to this question is "you can't, a char * is an address, you can't set it to some contents."

Marta Velasquez
Mar 9, 2013

Good thing I was feeling suicidal this morning...
Fallen Rib

hooah posted:

Ok, then how do I set argv[0] to the contents of intermediate, rather than its address?

code:
argv[i] = intermediate;
You can't copy arrays like this in both C and C++.

You don't need intermediate at all. You would essentially have to duplicate the code all over again to copy from intermediate to argv[i].

Out of curiosity, is there a reason you aren't using a standard library to copy the string rather than writing a custom for-loop? You can write something like:
code:
char argv[MAXPARAM][10];
for(size_t i = 0; i < MAXPARAM; i++)
{
    strcpy(argv[i], parameters[i].c_str());
}
Add error checking, of course, though.

Marta Velasquez fucked around with this message at 15:19 on Sep 19, 2014

hooah
Feb 6, 2006
WTF?

roomforthetuna posted:

Practical answer above in a simultaneous edit, but the real answer to this question is "you can't, a char * is an address, you can't set it to some contents."

Regarding your first solution, can I use new here, since I'm nominally working in C++?

contrapants posted:

Out of curiosity, is there a reason you aren't using a standard library to copy the string rather than writing a custom for-loop?
Which function from the standard library? strcpy doesn't work because I can't convert from std::string to char*. The same is true for strncpy and strlcpy as well.

contrapants, I just tried your edited code, and I got an error "cannot convert 'char (*)[10]' to 'char* const*' for argument '2' to 'int execvp(const char*, char* const*)'".

hooah fucked around with this message at 15:24 on Sep 19, 2014

raminasi
Jan 25, 2005

a last drink with no ice

hooah posted:

Which function from the standard library? strcpy doesn't work because I can't convert from std::string to char*. The same is true for strncpy and strlcpy as well.

contrapants, I just tried your edited code, and I got an error "cannot convert 'char (*)[10]' to 'char* const*' for argument '2' to 'int execvp(const char*, char* const*)'".

std::string has a c_str() member that will expose it as a const char *

hooah
Feb 6, 2006
WTF?

GrumpyDoctor posted:

std::string has a c_str() member that will expose it as a const char *

Right, that's what contrapants's code did, but I got the mentioned error. Unless you're suggesting using it in a different way?

raminasi
Jan 25, 2005

a last drink with no ice

hooah posted:

Right, that's what contrapants's code did, but I got the mentioned error. Unless you're suggesting using it in a different way?

Oh, sorry, I didn't follow the conversation closely enough. My bad.

Here's your general problem: execv* is requiring an array of pointers to c-style strings that it can use to modify those strings, but std::string::c_str() returns a pointer that can't be used for this purpose. I don't know if the first requirement is "real" or just something baked into the signature, but the second makes total sense: std::string doesn't want you mucking around in its internals.

The solution is to copy the string data somewhere so that execv* is allowed to mess around with it if it wants to. The "somewhere" is up to you. Once you've made all your copies, gather the pointers to the new locations, put them in an array, and pass that array off to execv*.

If you've got a fixed number of parameters n, and you know the maximum length of those parameters, your life is easiest, because you can just allocate n arrays that are big enough on the stack. Here's an example with two parameters of no more than 511 characters each:

C++ code:
std::string parameter1 = "param1";
std::string parameter2 = "param2";

char param1_buffer[512];
strcpy(param1_buffer, parameter1.c_str());
char param2_buffer[512];
strcpy(param2_buffer, parameter2.c_str());

char * command_buffer[] = {
	param1_buffer,
	param2_buffer,
	NULL
};

execv("I didn't pay attention to where this comes from", command_buffer);
(This is not production code; just use strcpy.)

raminasi fucked around with this message at 22:59 on Sep 19, 2014

hooah
Feb 6, 2006
WTF?
Thanks for the help, everyone. I went to office hours today and got it sorted out. Here's what I needed to do:
C++ code:
char *argv[10];
for(size_t i = 0; i < MAXPARAM; i++)
{
    char *param = (char *)malloc(sizeof(char));
    strcpy(param, parameters[i].c_str());
    argv[i] = param;
}

execvp(argv[0], argv);

b0lt
Apr 29, 2005

hooah posted:

Thanks for the help, everyone. I went to office hours today and got it sorted out. Here's what I needed to do:
C++ code:
char *argv[10];
for(size_t i = 0; i < MAXPARAM; i++)
{
    char *param = (char *)malloc(sizeof(char));
    strcpy(param, parameters[i].c_str());
    argv[i] = param;
}

execvp(argv[0], argv);

This code is wrong. You're allocating space for exactly one character, and hoping that whatever you're trampling on when you copy the string isn't too important.

hooah
Feb 6, 2006
WTF?

b0lt posted:

This code is wrong. You're allocating space for exactly one character, and hoping that whatever you're trampling on when you copy the string isn't too important.

The malloc part was from memory, since I didn't feel like rebooting my main computer into Ubuntu to copy exactly what I did. I probably added a * 10 after the sizeof.

b0lt
Apr 29, 2005

hooah posted:

The malloc part was from memory, since I didn't feel like rebooting my main computer into Ubuntu to copy exactly what I did. I probably added a * 10 after the sizeof.

Oh, then sure. Although, you should be mallocing a buffer that's the same size as the string you're copying in, and also sizeof(char) is 1 by definition.

Nippashish
Nov 2, 2005

Let me see you dance!

hooah posted:

rebooting my main computer into Ubuntu to copy exactly what I did.

Use a vm, for gods sake man.

TheFreshmanWIT
Feb 17, 2012

hooah posted:

Thanks for the help, everyone. I went to office hours today and got it sorted out. Here's what I needed to do:
C++ code:
char *argv[10];
for(size_t i = 0; i < MAXPARAM; i++)
{
    char *param = (char *)malloc(sizeof(char));
    strcpy(param, parameters[i].c_str());
    argv[i] = param;
}

execvp(argv[0], argv);

Your Malloc line needs to be 1+sizeof(char)*parameters[i].size(), otherwise you are going to have pretty severe memory issues.

Also, you have a few more issues.

First- argv being initialized to 10 needs to change. It likely needs to be MAXPARAM or something, or allocated with malloc/new.
Second- You're looping from i to MAXPARAM with no guarantee that the std::string collection 'parameters' contains that many items. You could likely be stepping outside of your parameters array.
Third- execvp uses nullptr(C++11 for goofy-ole- NULL) item as its sentinel value. currently argv is not initialized at all, and un-set values could very easily contain junk. You need to set unused argv values to nullptr.

TheFreshmanWIT fucked around with this message at 06:01 on Sep 20, 2014

Bonfire Lit
Jul 9, 2008

If you're one of the sinners who caused this please unfriend me now.

GrumpyDoctor posted:

Here's your general problem: execv* is requiring an array of pointers to c-style strings that it can use to modify those strings, but std::string::c_str() returns a pointer that can't be used for this purpose. I don't know if the first requirement is "real" or just something baked into the signature
It's not a real requirement; POSIX-1.2008 explicitly states that the argv and envp vectors and the strings they point to will never be modified by execv, outside of the process image getting replaced (at which point it doesn't matter anymore).
The reason for this signature is that ISO C doesn't allow you to implicitly convert char * const * to const char * const *, and due to backwards compatibility requirements (the execv functions had a char * prototype because const used to not exist in C), they chose char * const * for the new prototype.
You're perfectly fine casting a const char * const * array to char ** for use in execv only.

hooah
Feb 6, 2006
WTF?

TheFreshmanWIT posted:

Your Malloc line needs to be 1+sizeof(char)*parameters[i].size(), otherwise you are going to have pretty severe memory issues.

Also, you have a few more issues.

First- argv being initialized to 10 needs to change. It likely needs to be MAXPARAM or something, or allocated with malloc/new.
Second- You're looping from i to MAXPARAM with no guarantee that the std::string collection 'parameters' contains that many items. You could likely be stepping outside of your parameters array.
Third- execvp uses nullptr(C++11 for goofy-ole- NULL) item as its sentinel value. currently argv is not initialized at all, and un-set values could very easily contain junk. You need to set unused argv values to nullptr.

At least the third issue I'm pretty sure I fixed because I'd gotten a segfault; I just didn't remember that I'd fixed it. Thanks for the other suggestions.

Nippashish posted:

Use a vm, for gods sake man.

:effort:

Blotto Skorzany
Nov 7, 2008

He's a PSoC, loose and runnin'
came the whisper from each lip
And he's here to do some business with
the bad ADC on his chip
bad ADC on his chiiiiip

TheFreshmanWIT posted:

Your Malloc line needs to be 1+sizeof(char)*parameters[i].size(), otherwise you are going to have pretty severe memory issues.

Also, you have a few more issues.

First- argv being initialized to 10 needs to change. It likely needs to be MAXPARAM or something, or allocated with malloc/new.
Second- You're looping from i to MAXPARAM with no guarantee that the std::string collection 'parameters' contains that many items. You could likely be stepping outside of your parameters array.
Third- execvp uses nullptr(C++11 for goofy-ole- NULL) item as its sentinel value. currently argv is not initialized at all, and un-set values could very easily contain junk. You need to set unused argv values to nullptr.



sizeof(char) is a pointless operation, btw. comp.lang.c used to use it as a shibboleth.

FateFree
Nov 14, 2003

I'm coming from a java background so I'm having some difficulty understanding a C library. I'm trying to execute a simple SHA256 hash on a String, but I can't really understand the method signature. If someone could provide a small main method that invokes this code with a String of "hello" and maybe prints out the byte array I would be eternally grateful.

Here is the library: http://nayuki.eigenstate.org/page/fast-sha2-hashes-in-x86-assembly
Here is the source code: http://nayuki.eigenstate.org/res/fast-sha2-hashes-in-x86-assembly/sha256.c

Qwertycoatl
Dec 31, 2008

FateFree posted:

I'm coming from a java background so I'm having some difficulty understanding a C library. I'm trying to execute a simple SHA256 hash on a String, but I can't really understand the method signature. If someone could provide a small main method that invokes this code with a String of "hello" and maybe prints out the byte array I would be eternally grateful.

Here is the library: http://nayuki.eigenstate.org/page/fast-sha2-hashes-in-x86-assembly
Here is the source code: http://nayuki.eigenstate.org/res/fast-sha2-hashes-in-x86-assembly/sha256.c

That function just does the main computation part of SHA256. I'd expect it to be used as part of someone's implementation of a user-level hashing function, rather than something you'd call directly. (If you look at the pseudocode here, your function seems to basically be the bit inside the "for each chunk".

raminasi
Jan 25, 2005

a last drink with no ice

Bonfire Lit posted:

It's not a real requirement; POSIX-1.2008 explicitly states that the argv and envp vectors and the strings they point to will never be modified by execv, outside of the process image getting replaced (at which point it doesn't matter anymore).
The reason for this signature is that ISO C doesn't allow you to implicitly convert char * const * to const char * const *, and due to backwards compatibility requirements (the execv functions had a char * prototype because const used to not exist in C), they chose char * const * for the new prototype.
You're perfectly fine casting a const char * const * array to char ** for use in execv only.

Good to know, thanks!

FateFree
Nov 14, 2003

Qwertycoatl posted:

That function just does the main computation part of SHA256. I'd expect it to be used as part of someone's implementation of a user-level hashing function, rather than something you'd call directly. (If you look at the pseudocode here, your function seems to basically be the bit inside the "for each chunk".

I see maybe thats why I've been having trouble understanding. They also include a test class, maybe I can use the method at the end?

The test class is here: http://nayuki.eigenstate.org/res/fast-sha2-hashes-in-x86-assembly/sha256test.c

Qwertycoatl
Dec 31, 2008

FateFree posted:

I see maybe thats why I've been having trouble understanding. They also include a test class, maybe I can use the method at the end?

The test class is here: http://nayuki.eigenstate.org/res/fast-sha2-hashes-in-x86-assembly/sha256test.c

Yes, that looks more like it.

code:
const char * input = "Hello";
uint32_t output[8];
int i;
sha256_hash(input, strlen(input), output);
for (i = 0; i < 8; i++)
{
    printf("%08X", output[i]);
}
(it's possible I screwed up the endianness printing the output, check against something you know the hash value of)

Bruegels Fuckbooks
Sep 14, 2004

Now, listen - I know the two of you are very different from each other in a lot of ways, but you have to understand that as far as Grandpa's concerned, you're both pieces of shit! Yeah. I can prove it mathematically.
edit: beaten

fritz
Jul 26, 2003

Blotto Skorzany posted:

comp.lang.c used to use it as a shibboleth.

Which way?

FateFree
Nov 14, 2003

Qwertycoatl posted:

Yes, that looks more like it.

Excellent! I checked it against the hash from a java application and it matches, thank you very much. I'm now going to attempt to invoke this from a java app via JNI, so I'll probably have more questions. One quick one, I noticed a lot of method signatures that take an array, and also take the length of the array as the next argument. In java I could just pass the array and do a length check on it rather than force another argument, but I imagine because C is not managed the same thing is not possible?

roomforthetuna
Mar 22, 2005

I don't need to know anything about virii! My CUSTOM PROGRAM keeps me protected! It's not like they'll try to come in through the Internet or something!
A more challenging question - working with protobuffers, I want to do some crazy poo poo with mapping elements of a protobuffer tree to some other affiliated data.

For example, if the structure of protobuffers is
code:
message Foo {
  optional Bar bar = 1;
  repeated Baz baz = 2;
}

message Bar {
  optional int32 butts = 1;
}

message Baz {
  repeated int32 spaz = 1;
  optional int32 graz = 2;
}
I want to be able to associate the path ->bar()->butts() with, say, "This many butts." and ->baz()->spaz() with, say, "You are a fish."

I've achieved this using vector<int> to represent paths, so I can initialize a path like { Foo::kBarFieldNumber, Bar::kButtsFieldNumber }, which at least gives me compile time errors if I try to make a path out of complete nonsense, and I can *use* the path using the protobuffer reflection interface, but what I'm really hoping for is something A. a bit less verbose, and B. more type safe. (ie. resilient to accidental { Foo::kBarFieldNumber, Baz::kSpazFieldNumber }, which is the identical path to the first example and should be invalid because the first path element doesn't lead to the class of the second path element.)

So ideally what I'd have is a path class templated on the base type (Path<Foo>) that could be initialized like
code:
Path<Foo> my_path<bar, butts>();
or
Path<Foo> my_path<bar, Bar, butts>();
or
Path<Foo> my_path<Bar, Bar, Butts>();
Unfortunately templates can't do what macros can (ie. turning 'Bar' into 'kBarFieldNumber'), and I don't think they can even do the other thing macros can, turning <A, B> into A::B. And unfortunately even if I *could* do macro-style concatenation, I still couldn't really get the type of the submessage (I think I could get type Bar from some std::trickery around Foo::bar(), but getting the return type off Foo::baz() isn't going to get me a Baz because it's a repeated field.)

Is there any way to improve on the way I'm already doing it, either reducing verbosity or improving type safety, without compromising the other one?

I could reduce verbosity by initializing a path like { "bar", "spaz" } and then getting the numbers via reflection in the initializer, but the compile-time type safety would be worse. On the other hand the run-time type safety might be "better" in that it would explode in the path constructor rather than creating a valid but incorrect path.

roomforthetuna fucked around with this message at 22:37 on Sep 20, 2014

FateFree
Nov 14, 2003

Qwertycoatl posted:

code:
const char * input = "Hello";
uint32_t output[8];
sha256_hash(input, strlen(input), output);

I am successfully using this code to execute a hash. However my next step is trying to return the result to a java application via JNI. The method signature looks like this, but I can't figure out how to convert the output array to a jbytearray. Here is what I tried after seeing some examples:

code:
JNIEXPORT jbyteArray JNICALL Java_com_fatefree_WNative_getSHA256Bytes(JNIEnv * env, jobject jobj, jstring jstr) {
	const char * input = (*env)->GetStringUTFChars(env, jstr, 0);
	uint32_t output[8];
	sha256_hash(input, strlen(input), output);
	
	// Need help with this part - how to correctly convert the output array to a jbytearray?
	jbyte* buf = new jbyte[size];
	memset (buf, output, size);
	jbyteArray ret = env->NewByteArray(size);
	env->SetByteArrayRegion (ret, 0, size, buf);
	delete[] buf;
	return ret;
}	

Qwertycoatl
Dec 31, 2008

FateFree posted:

Excellent! I checked it against the hash from a java application and it matches, thank you very much. I'm now going to attempt to invoke this from a java app via JNI, so I'll probably have more questions. One quick one, I noticed a lot of method signatures that take an array, and also take the length of the array as the next argument. In java I could just pass the array and do a length check on it rather than force another argument, but I imagine because C is not managed the same thing is not possible?

That's right, if you pass an array into a function in C, the callee can't tell how long it is and must be told via a separate parameter.

I have no idea how JNI stuff works I'm afraid.

Subjunctive
Sep 12, 2006

✨sparkle and shine✨

FateFree posted:

code:

	uint32_t output[8];
	jbyte* buf = new jbyte[size];
	memset (buf, output, size);

My JNI is really rusty, and tbh I'd like to keep it that way, but what do you want the bytes to represent? Why not return an int array instead?

Volguus
Mar 3, 2009

FateFree posted:

I am successfully using this code to execute a hash. However my next step is trying to return the result to a java application via JNI. The method signature looks like this, but I can't figure out how to convert the output array to a jbytearray. Here is what I tried after seeing some examples:

code:
JNIEXPORT jbyteArray JNICALL Java_com_fatefree_WNative_getSHA256Bytes(JNIEnv * env, jobject jobj, jstring jstr) {
	const char * input = (*env)->GetStringUTFChars(env, jstr, 0);
	uint32_t output[8];
	sha256_hash(input, strlen(input), output);
	
	// Need help with this part - how to correctly convert the output array to a jbytearray?
	jbyte* buf = new jbyte[size];
	memset (buf, output, size);
	jbyteArray ret = env->NewByteArray(size);
	env->SetByteArrayRegion (ret, 0, size, buf);
	delete[] buf;
	return ret;
}	

First of all, there are plenty of pure java libraries out there that can calculate sha-256 from a string.
But, for the sake of argument, let's assume that you have to (somehow) use JNI, and that function specifically (sha256_hash). Apparently, that function is giving you a uint32_t array. Now, each element of that array is (duh) a uint32_t, that is an unsigned 4 bytes integer. You cannot (normally) convert that to a byte array, since each byte is , well, 1 byte, not 4 (well, you can cast it if you really want, but then you're endianess dependent, it's more headache than it's worth. plus ... who's gonna delete it? The Java gc won't know about it).
You cannot even use a java int, since int in java is a 32-bit signed two's complement integer. Long would be the least you can do. And you most likely cannot just write memset, but instead go through each element of the array and assign the value to the other array (longArray[i] = output[i]). Or, maybe memcpy could work somehow, but it such a freaking pain.

My advice would be to just go to a java library that does that for you. If you're worried about performance, then Java is not the right language to write your program in. Use plain C or C++.

hooah
Feb 6, 2006
WTF?
It's been a couple months since I used make, and apparently I've pretty much forgotten everything. For this makefile, on the line g++ -Wall -std=c++11 mainBFS.cpp NodeBFS.o -o BFS.o, I'm getting an error "NodeBFS.o: file not recognized: File format not recognized". Why would this be?

code:
BFS: BFS.o NodeBFS.o
	g++ -Wall -std=c++11 BFS.o NodeBFS.o -o BFS

BFS.o: mainBFS.cpp NodeBFS.o
	g++ -Wall -std=c++11 mainBFS.cpp NodeBFS.o -o BFS.o

NodeBFS.o: NodeBFS.cpp NodeBFS.h
	g++ -c -Wall -std=c++11 NodeBFS.cpp NodeBFS.h -o NodeBFS.o

nielsm
Jun 1, 2009



You're making things more complex than they should be. Use the built-in rules of Make. I believe this should work:

code:
CXXFLAGS=-Wall -std=c++11

BFS: BFS.o NodeBFS.o
(But your actual problem is that you're including objects and sources on a single commandline. Your BFS.o command is missing the -c parameter and must not include NodeBFS.o on the commandline because you aren't linking the program at that time. Also the compiler probably won't like to compile NodeBFS.h as a source file, or it will produce very little results from it.)

FateFree
Nov 14, 2003

rhag posted:

First of all, there are plenty of pure java libraries out there that can calculate sha-256 from a string.
But, for the sake of argument, let's assume that you have to (somehow) use JNI, and that function specifically (sha256_hash). Apparently, that function is giving you a uint32_t array. Now, each element of that array is (duh) a uint32_t, that is an unsigned 4 bytes integer. You cannot (normally) convert that to a byte array, since each byte is , well, 1 byte, not 4 (well, you can cast it if you really want, but then you're endianess dependent, it's more headache than it's worth. plus ... who's gonna delete it? The Java gc won't know about it).
You cannot even use a java int, since int in java is a 32-bit signed two's complement integer. Long would be the least you can do. And you most likely cannot just write memset, but instead go through each element of the array and assign the value to the other array (longArray[i] = output[i]). Or, maybe memcpy could work somehow, but it such a freaking pain.

My advice would be to just go to a java library that does that for you. If you're worried about performance, then Java is not the right language to write your program in. Use plain C or C++.

Thanks for the insight. I'm aware of the java libraries but this is more of a JNI exercise that I can verify against the java libraries. But I am also trying to maximize performance. I think just to get it working I'll pass back a hex string to java and then i won't need to worry about these data types. Qwerty gave me a print loop which displays the correct hex, so I'd like to try to append that to a string and return it as the result of the method. Can I utilize the printf function somehow, since it seems so compact? Here is what he gave me:

code:
for (i = 0; i < 8; i++)
{
    printf("%08X", output[i]);
}

Volguus
Mar 3, 2009

FateFree posted:

Thanks for the insight. I'm aware of the java libraries but this is more of a JNI exercise that I can verify against the java libraries. But I am also trying to maximize performance. I think just to get it working I'll pass back a hex string to java and then i won't need to worry about these data types. Qwerty gave me a print loop which displays the correct hex, so I'd like to try to append that to a string and return it as the result of the method. Can I utilize the printf function somehow, since it seems so compact? Here is what he gave me:

code:
for (i = 0; i < 8; i++)
{
    printf("%08X", output[i]);
}

You could use sprintf on a char array, and put that into a string. Something like:
code:
char *buf = (char*)malloc(255);
for (i = 0; i < 8; i++)
{
    char tmp[10];
    sprintf(tmp,"%08X", output[i]);
    strcat(buf, tmp);
}
jstring jstrBuf = (*env)->NewStringUTF(env, buf);
Or maybe using std::stringstream would be easier (formatting can be specified with fill and width : stream << std::setfill('0') << std::setw(2) << value;)

nielsm
Jun 1, 2009



rhag posted:

You could use sprintf on a char array, and put that into a string. Something like:
code:
char *buf = (char*)malloc(255);
for (i = 0; i < 8; i++)
{
    char tmp[10];
    sprintf(tmp,"%08X", output[i]);
    strcat(buf, tmp);
}
jstring jstrBuf = (*env)->NewStringUTF(env, buf);
Or maybe using std::stringstream would be easier (formatting can be specified with fill and width : stream << std::setfill('0') << std::setw(2) << value;)

If you were doing it C-style, avoid the constant strcat() calls and instead use the fact that sprintf() returns the number of characters written (excluding terminating nul).

C++ code:
char buf[80];
char *wptr = buf;
for (int i = 0; i < 8; i++)
{
  wptr += sprintf(wptr, "%08X", output[i]);
}
jstring jstrBuf = (*env)->NewStringUTF(env, buf);

FateFree
Nov 14, 2003

nielsm posted:

If you were doing it C-style, avoid the constant strcat() calls and instead use the fact that sprintf() returns the number of characters written (excluding terminating nul).

C++ code:
char buf[80];
char *wptr = buf;
for (int i = 0; i < 8; i++)
{
  wptr += sprintf(wptr, "%08X", output[i]);
}
jstring jstrBuf = (*env)->NewStringUTF(env, buf);

Works great! Thank you guys so much for your help

Boz0r
Sep 7, 2006
The Rocketship in action.
Am I dumb, or don't the ReSharper keyboard shortcuts not work properly in Visual Studio 2013?

Adbot
ADBOT LOVES YOU

FateFree
Nov 14, 2003

Another question for you guys. I'm calling a RMD160 hash function in C, and I'm comparing the results to the same function in Java. If I take a simple word with standard characters, the hex values are identical, and the hash output is identical. However some hex values, even though they are identical in both codebases, will produce different hashes. I suspect this has to do with the character sets or strange symbols? How can I ensure the same hash is return from the C program that I'm seeing in the Java program?

  • 1
  • 2
  • 3
  • 4
  • 5
  • Post
  • Reply