Shop OBEX P1 Docs P2 Docs Learn Events
Byte array to String — Parallax Forums

Byte array to String

stampedstamped Posts: 68
edited 2007-07-20 10:45 in Propeller 1
I am writing a very simple string library that performs some basic functions. I have got concatenation working through byte arrays, but I am wanting to convert the byte array back to a string. I am not having any luck with bytemove.
·
PUB tester | temp1, temp2, length1, length2, val[noparse][[/noparse]7], count, copystr
    temp1 := string("test")
    temp2 := string("ing")
    
    ' CONVERTING FROM A STRING TO A BYTE ARRAY (pre-defined hard coded size -> painful)     
    length1 := strsize(temp1)   ' 4
    count := 0
    repeat length1
      val[noparse][[/noparse]count++] := byte[noparse][[/noparse]temp1++]
    'All copied into val
    length2 := strsize(temp2)
    repeat length2
      val[noparse][[/noparse]count++] := byte[noparse][[/noparse]temp2++]    
 
    ' OUTPUTTING THE CONCATENATED STRINGS
    count := 0
    repeat 7
      Serial.tx(val[noparse][[/noparse]count++])
    Serial.str(string(" "))
 
    [b]' NOTE THIS IS CONVERTING BACK TO A STRING
[/b]    [b]bytemove(@copystr, @val, strsize(@val)+1)    [/b]' Even tried hard coding 7,6,5,4..
     Serial.str(copystr)         ' Outputs garbage, have also tried passing the address (@copystr) without luck



Another couple of questions:

- I·am also unsure how to determine the length of an array, such as the number of characters in a byte array?
- I would also like to be able to set the size of an array after the fact, ie create one in the body of the method rather than pre-define in the method header.

Post Edited (stamped) : 7/17/2007 2:52:33 PM GMT

Comments

  • Mike GreenMike Green Posts: 23,101
    edited 2007-07-17 14:57
    "val" is an array of longs while strings are arrays of bytes. It all works when you're using a subscript which the Spin interpreter adjusts (multiplies) as needed by 2 or 4 for words or longs. The byte move won't work because you're trying to cram 4 bytes per character into one byte per character.

    You can't determine the size of a declared array. It's not recorded anywhere. People often define a named constant with the string array size(s) they plan to use, declare the byte arrays using the name, and have that name to use in bytemoves and similar such things. That's the best you can do.

    If you're going to make up an object that handles strings as byte arrays, you can establish any conventions you want. The two most common are: 1) Using a zero byte as a string terminator (the "C" convention); 2) Using a length byte as the first byte of the array with the characters following the length (the "Pascal" convention). Neither provide for an indication of the allocated string length. This can be done with a byte preceeding the string area in memory so we have: <size of array> <length of string> <string> or <size of array> <string> <terminator>.

    If you want to allocate space in your code (dynamically) you're describing a storage allocator. There's none built into Spin. Someone did develop a string manipulation object (check the Object Exchange and the forum) which has a simple storage allocator as part of it. You still have to declare the actual storage pool somewhere. There are all sorts of issues that come up around what to do to reuse the space for strings that aren't used anymore and how to figure that out. Have a look at the Wikipedia under "dynamic memory allocation" for a discussion.

    Post Edited (Mike Green) : 7/17/2007 3:09:39 PM GMT
  • stampedstamped Posts: 68
    edited 2007-07-17 15:24
    Thanks Mike. The ideas noted make a lot of sense and I will use the C and Pascal conventions (or similar) in my simple string/convenience library. The length idea is a good one for arrays and something I had not considered.

    With regards to the declaration of var as a long, how do I define the methods variable "var" as a Byte array (can I cast it)? I am new to Spin, so I am slowly coming to grips with the semantics of the language.

    As far as dynamic storage allocation is concerned, if I cannot create a variable on the fly I think this would be a bit beyond my abilities at this stage. Unless I could declare a global variable and use that as the size of the method variable (I assume this will not work):

    PUB tester new_array | val[noparse][[/noparse]global_var_size]
    return val
  • Mike GreenMike Green Posts: 23,101
    edited 2007-07-17 16:04
    You can't declare a local variable as a byte ... they're all longs. If "var" is a local array, you can use it as a byte array with "BYTE[noparse][[/noparse]@var][noparse][[/noparse] i ]".

    Again, Spin does not do dynamic memory allocation. All arrays have constant bounds. This lets the compiler always know the address of the variable (for local variables, the address will change, but the relative stack offset is fixed). Since Spin supports pointers, you are free to do your own dynamic memory allocation, but there's no other support for it.
  • mcstarmcstar Posts: 144
    edited 2007-07-18 04:05
    Something I've had to come to terms with where spin and the propeller is concerned, is that unlike some other strongly typed languages, there really is no "data type" in spin. You can kinda say there are bytes, longs and characters, but really, there are just bytes (especially when talking about main memory). There is no memory manager that restricts the "types". So, you have to create objects that enforce the concept (ie data type) that you plan to use. With spin (and especially when using Gear) I'm seeing my own programming move more and more to the lowest levels of bit and byte banging. I'm starting to see the beauty in directly modifying shared memory with pointers and byte[noparse]/noparse. Spin is really a very thin layer of abstraction on top of assembly (especially compared to C# or Java). It's just enough not to get in the way of writing really tight code that effectively uses multiple Cogs well. Parallax is to be commending for bringing this fantastic tool to us.
  • Mike GreenMike Green Posts: 23,101
    edited 2007-07-18 04:24
    Think about C as it originally was defined and used. Except for the inclusion of floating point which is much more limited in Spin, there's not a lot of difference between the two conceptually. Prior to the development of Standard C, few implementations had things like bit fields and floating point was optional. There was little to no use of strong typing. Languages like Pascal and its derivatives were partially reactions to the generally weak typing that was widely used at the time (including in C).
  • stampedstamped Posts: 68
    edited 2007-07-18 10:47
    I tested the "casting" (that Mike mentioned above) to a byte, but now I just get garbage output when I try sending to the debug terminal:

    ' CONVERTING FROM A STRING TO A BYTE ARRAY    
        length1 := strsize(temp1)   ' 4
        count := 0
        repeat length1
          'val[noparse][[/noparse]count++] := byte[noparse][[/noparse]temp1++]              ' Removed per Mikes notes
          byte[noparse][[/noparse]@val][noparse][[/noparse]count++] := byte[noparse][[/noparse]temp1++]        ' "Casting"    
    'All copied into val                          
        length2 := strsize(temp2)
        repeat length2
          'val[noparse][[/noparse]count++] := byte[noparse][[/noparse]temp2++]              ' Note re-implemented using byte[noparse][[/noparse]@val..
          byte[noparse][[/noparse]@val][noparse][[/noparse]count++] := byte[noparse][[/noparse]temp2++]    
        Serial.str(string(" val: "))
        ' OUTPUTTING THE CONCATENATED STRINGS
        count := 0
        repeat 7
          Serial.tx(@val[noparse][[/noparse]count++])
        Serial.str(string(" "))
    

    If you could point me to some documentation that thoroughly explains the typing I would appreciate it. At the moment, it is one step forward, and two backwards.

    Post Edited (stamped) : 7/18/2007 11:14:25 AM GMT
  • Mike GreenMike Green Posts: 23,101
    edited 2007-07-18 12:20
    You need to use "Serial.tx(byte[noparse][[/noparse]@val][noparse][[/noparse]count++])". The rest appears to be correct.
  • stampedstamped Posts: 68
    edited 2007-07-18 12:44
    Thanks again Mike. I actually thought I tried that. I just tested it out and it is all working.
    For anyone else reading this, I think that the reason it works, is because the "byte[noparse][[/noparse]@val]"· passes the location in memory·of the data in the "val" array, and the count++ sets the offset (starting position)·to the data that sits in val.

    Another question Mike that I have come across is why does the following not work (prints garbage):
        ' Note that string_buffer is defined in the VAR block as byte string_buffer[noparse][[/noparse]20]
     
        temp1 := string("test")
        temp2 := string("ing")
     
        bytefill(@string_buffer, 0, 20)     ' Fill the array with 0's
        bytemove(@string_buffer, @temp1, strsize(temp1))
        bytemove(@string_buffer + strsize(temp1), @temp2, strsize(temp2))
        
        Serial.str(string("string_buffer is:"))
     
        count := 0
    
        repeat 7
          Serial.tx(byte[noparse][[/noparse]@string_buffer][noparse][[/noparse]count++])
    

    I hope my 20 questions are helping some other newbies understand Spin [noparse]:)[/noparse]
  • Mike GreenMike Green Posts: 23,101
    edited 2007-07-18 12:56
    The problem you're running into is that you're storing the address of string constants in temp1 and temp2 and you're also using byte array variables with the "@" operator and you're mixing up the two. In the bytemoves, you have @temp1 and @temp2. In this case, you have to leave off the "@". You're copying the addresses of the string constants into string_buffer, not the strings themselves.

    byte[noparse][[/noparse]@val] works because it is a byte array that begins at the same memory location as the variable "val" regardless of how "val" is declared. It's a combination of pointer dereferencing and casting if you want to use C terminology.

    Post Edited (Mike Green) : 7/18/2007 1:00:55 PM GMT
  • stampedstamped Posts: 68
    edited 2007-07-18 13:12
    It is a little confusing but I think I am getting there. I understand that the @val points to the memory location. What does "val" alone point to? Does it hold the first character (offset 0) in the byte array?
  • Mike GreenMike Green Posts: 23,101
    edited 2007-07-18 13:22
    "val" by itself refers to the first element of the array (offset 0). If "val" is declared as byte, it's a byte. If "val" is declared as a long array, it's a long.
  • stampedstamped Posts: 68
    edited 2007-07-18 13:28
    ok, that is great Mike. I have got it. I am scribbling notes down (it would be great to have an official Propeller Wiki), and it all makes sense now. Thanks for your help (again). No doubt you will see me back here in a day or two with a new question turn.gif
  • mahjonggmahjongg Posts: 141
    edited 2007-07-20 10:45
    A propeller wiki exists!
    It was started a few days ago by HYDRA enthusiasts, and primarily is focused on video drivers and games, but there is no reason why it cant be used for more generic propeller issues too.

    It can be found here propeller.wikispaces.com/.

    Ill make a more formal announcement on a separate message too, so it's more visible for casual readers of this forum.

    Mahjongg
Sign In or Register to comment.