PDA

View Full Version : Strange method behavior, it will work locally, but not as an object



turbosupra
10-21-2011, 01:31 PM
I'm using Kye's strings method and when I send the following I'm getting strange behavior and was hoping someone could shed some light as to why. I tried to make this as simple as possible, but if it is confusing, I'll be happy to post my entire code, please let me know.

Whenever I send a string that has 2 or more characters in front of the delimiterPositionForName (in this case an equals sign) it will work

So 01=mot or 44=mot

But anytime I run it as an object and it is a single digit, it will not find the delimiter and returns a -1.



DOESN'T WORK
OBJ
pst : "Parallax Serial Terminal"
strings : "Strings"
com : "serialCom"

l_variableReversed := "3=mot"
b_delimiter[0] := "="
b_delimiter[1] := 0

l_returnTemp := strings.StrPos(l_variableReversed, @b_delimiter, 0)

l_returnTemp is -1 (or it cannot find it)


when I run it locally



DOES WORK
OBJ
pst : "Parallax Serial Terminal"
strings : "Strings"
com : "serialCom"

l_variableReversed := "3=mot"
b_delimiter[0] := "="
b_delimiter[1] := 0

l_returnTemp := StrPos(l_variableReversed, @b_delimiter, 0)

l_returnTemp is 1


Does anyone have any theories as to why? Thanks for reading.


This code below always works locally or as a method because there are at least 2 characters in front of the delimiter, in this case "31".



ALWAYS WORKS
OBJ
pst : "Parallax Serial Terminal"
strings : "Strings"
com : "serialCom"

l_variableReversed := "31=mot" (or anything that is more than 1 digit)
b_delimiter[0] := "="
b_delimiter[1] := 0

l_returnTemp := strings.StrPos(l_variableReversed, @b_delimiter, 0) OR l_returnTemp := StrPos(l_variableReversed, @b_delimiter, 0)

kuroneko
10-21-2011, 01:54 PM
The only string object with that method I could find in the OBEX is from Brandon Nimon and it works as advertised.

OBJ
strings : "Strings"

VAR
long l_variableReversed
byte b_delimiter[2]

PUB null

l_variableReversed := string("3=mot")
b_delimiter[0] := "="
b_delimiter[1] := 0

dira[16..23]~~
outa[16..23] := strings.StrPos(l_variableReversed, @b_delimiter, 0)

waitpne(0, 0, 0)
I don't know how you get away with l_variableReversed := "3=mot" (doesn't compile) but the code above works for me and returns 1.

Mike Green
10-21-2011, 01:59 PM
The assignment l_variableReversed := "j_mot" is not legal. I don't know why the compiler let it pass. You can have single characters between quotes and the compiler will use the numeric value of the single character which isn't what you want here. The proper form is

l_variableReversed := string("31=mot")

This sets l_variableReversed to the address of the first byte of the string which is what you want here. The string is stored as a byte array with a zero byte appended to it to mark the end.

turbosupra
10-21-2011, 02:09 PM
Hi Kuroneko,

Thanks for the reply that was a typo, I apologize for that. Since I was trying to simply my code, I missed that when typing.

The work firewall is blocking me from uploading the method object.

turbosupra
10-21-2011, 02:18 PM
Hi Mike,

So why if I call this method as an attached object and strAddr (in the method below) starts with a single numeric value before the delimiter, will the method fail ... but if strAddr starts with 2 or more numeric values, will the method succeed?

Then when I call it locally, it works with a single digit?

I can post my entire code, just not as an attached set of files, if that helps.





PUB StrPos (strAddr, searchAddr, offset) | size, searchsize
{{Returns location of first occurrence of search in str, returns -1 if search is not found.
NOTE: counting starts at 0. 0 can be returned if search is found at first character of str.
Faster than strstr if just searching for a string inside another str.
StrPos("ABCDEFGHIJK","GH",0)
Output: 6
}}
size := strsize(strAddr) + 1
searchsize := strsize(searchAddr)
REPEAT UNTIL (offset + searchsize > size)
IF (strcomp(StrParse(strAddr, offset++, searchsize), searchAddr)) ' if string search found
RETURN offset - 1 ' return byte location
RETURN -1



PUB StrParse (strAddr, start, count)
{{Returns part of a string for count bytes starting from start byte.
This is a faster and simpler version of SubStr.
NOTE: forward counting starts at 0.
example: char-position 01234567890
string: ABCDEFGHIJK
SubParse("ABCDEFGHIJK",4,1)
Output: "E"
}}
'bytefill(@ostr, 0, count)
count <#= constant(STR_MAX_LENGTH - 1)
bytemove(@ostr, strAddr + start, count) ' just move the selected section

ostr[count] := 0 ' terminate string
RETURN @ostr





The assignment l_variableReversed := "j_mot" is not legal. I don't know why the compiler let it pass. You can have single characters between quotes and the compiler will use the numeric value of the single character which isn't what you want here. The proper form is

l_variableReversed := string("31=mot")

This sets l_variableReversed to the address of the first byte of the string which is what you want here. The string is stored as a byte array with a zero byte appended to it to mark the end.

turbosupra
10-21-2011, 02:30 PM
Ok,

I believe this indicates what the problem is, although I'm not 100% sure yet what it is.

I'm getting "3=mot" as a return value from another method. It is a variable that is set to a long (l_variableReversed ). In my example I hard coded it to simplify how long the code was and to try and make it easier to understand (which is where the typo came from). When I hard coded it like your example, it did indeed work.

The difference is between

(long) l_variableReversed := string("3=mot")

and (long) l_variableReversed := method return value, which I believe is a "long"

Can anyone expand upon this and tell me if I'm onto something or not? If this is indeed the issue, how can I correct it?




The only string object with that method I could find in the OBEX is from Brandon Nimon and it works as advertised.

OBJ
strings : "Strings"

VAR
long l_variableReversed
byte b_delimiter[2]

PUB null

l_variableReversed := string("3=mot")
b_delimiter[0] := "="
b_delimiter[1] := 0

dira[16..23]~~
outa[16..23] := strings.StrPos(l_variableReversed, @b_delimiter, 0)

waitpne(0, 0, 0)
I don't know how you get away with l_variableReversed := "3=mot" (doesn't compile) but the code above works for me and returns 1.

Mike Green
10-21-2011, 02:46 PM
Where's ostr?

When you use the Strings object, ostr is allocated in the storage area for the object. When you use the StrParse method locally, you have to provide that and it has to be big enough to hold the largest possible result string.

turbosupra
10-21-2011, 03:03 PM
I have the same values in both files, whether I run it locally or as an attached object. Would you like the whole code in text format?

Also, can you explain the programmatic difference between these two? Is there a "toString" method, so I could say l_variableReversed := l_variableReversed.ToString or something like that?

(long) l_variableReversed := string("3=mot")

and (long) l_variableReversed := method return value, which I believe is a "long"




CON
STR_MAX_LENGTH = 128


VAR
BYTE ostr[STR_MAX_LENGTH]
BYTE ostr2[STR_MAX_LENGTH] ' used for StrReplace only





Where's ostr?

When you use the Strings object, ostr is allocated in the storage area for the object. When you use the StrParse method locally, you have to provide that and it has to be big enough to hold the largest possible result string.

Mike Green
10-21-2011, 04:47 PM
I guess I'm still confused about what you're trying to do, what you're starting with, and what the "strange behavior" is.

Spin does not really have string values. It has 32-bit signed integers that are called longs. There are 16-bit variables (words) and 8-bit variables (bytes) as well as arrays of all of these. No automatic sign extension is done when fetching values from storage units smaller than 32-bits. String constants are stored as byte arrays with a zero byte as a terminator and the address of the first byte is what is used as the string "value". Various library routines that use string values (like the String object) use the same convention. Method parameters, results, and local variables are all longs.

Spin does allow single quoted characters in expressions and the value of that quoted character is the numeric value of the character in the ASCII character set. StrPos expects its first two parameters to be strings, in other words, the addresses of characters stored in byte arrays in memory. You would call StrPos as follows:

outa[16..23] := strings.StrPos(string("3=mot"), string("="), 0)

If you declare

var byte l_stringReversed[10], delimiter[3]
var long l_variableReversed

you could then write (in some method)

l_variableReversed := @l_stringReversed ' get the address of the string value
bytemove(l_variableReversed, string("3=mot"), 6) ' initialize the string value
bytemove(@delimiter, string("="), 2) ' initialize the delimiter value

dira[ 16..23 ]~~
outa[ 16..23 ] := StrPos( l_variableReversed, @delimiter, 0 )

turbosupra
10-21-2011, 05:58 PM
I guess I'm still confused about what you're trying to do, what you're starting with, and what the "strange behavior" is.


Hi Mike,

Thanks for the reply. The strange behavior is that when I use the StrPos (and StrParse) methods locally, they will work regardless of how many characters/bytes precede the delimiter in the StrPos method.

When I use the StrPos method by attaching it inside of the Strings object (Strings.StrPos)



OBJ
strings : "Strings"


it will only work if the character/byte count is 2 or more, that precede the delimiter in the Strings.StrPos method.




Spin does not really have string values. It has 32-bit signed integers that are called longs. There are 16-bit variables (words) and 8-bit variables (bytes) as well as arrays of all of these. No automatic sign extension is done when fetching values from storage units smaller than 32-bits. String constants are stored as byte arrays with a zero byte as a terminator and the address of the first byte is what is used as the string "value". Various library routines that use string values (like the String object) use the same convention. Method parameters, results, and local variables are all longs.


If I understand this correctly, if a string is "This is a string", the variable would be stringName[128] and the string method would want the memory address of the first "T". After that, it would read each concurrent byte, until it arrived at a byte with all zeros, 00000000 and that is how it determines the length of the string byte array and where it ends, correct?




Spin does allow single quoted characters in expressions and the value of that quoted character is the numeric value of the character in the ASCII character set.


This is because it stores it as a single byte, followed by a byte terminator of 00000000 ?




StrPos expects its first two parameters to be strings, in other words, the addresses of characters stored in byte arrays in memory. You would call StrPos as follows:

outa[16..23] := strings.StrPos(string("3=mot"), string("="), 0)

If you declare

var byte l_stringReversed[10], delimiter[3]
var long l_variableReversed

you could then write (in some method)

l_variableReversed := @l_stringReversed ' get the address of the string value
bytemove(l_variableReversed, string("3=mot"), 6) ' initialize the string value
bytemove(@delimiter, string("="), 2) ' initialize the delimiter value

dira[ 16..23 ]~~
outa[ 16..23 ] := StrPos( l_variableReversed, @delimiter, 0 )

I will have to try this code. Would you mind if I emailed you my code, so you could see the changing behavior?

Mike Green
10-21-2011, 07:04 PM
"Spin does not really have string values ..." Yes, you're correct except that the length of the array allocated is the number of characters in the string plus 1.

"Spin does allow single quoted ..." No, the constant value is actually encoded in the Spin interpretive instructions since there's an instruction that does a push immediate value for 8-bit values. There's no byte terminator since this really isn't considered a string, just a single character value.