Shop OBEX P1 Docs P2 Docs Learn Events
Creating a Database — Parallax Forums

Creating a Database

william chanwilliam chan Posts: 1,326
edited 2009-01-22 00:48 in Propeller 1
Hi,

Let's say I need a create a database of 3 field records in a DAT section.

The fields are

1. Enumeration (long)
2. String (length of bytes 15-30)
3. Price in dollars (long)

How do I declare such a database in the DAT section?
Thanks.

▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
www.fd.com.my
www.mercedes.com.my

Comments

  • mctriviamctrivia Posts: 3,772
    edited 2009-01-21 01:45
    directly you don't you can use a block of ram and put data in it any way you want as long as your code can interpret the data. say

    DAT
    data res 300
    
    



    then make each entry 10 longs in length or use a searchable delimiter to show the seperation, or make a table of content. fixed length is best and easiest if you have a fairly fixed length to your field values.
  • mparkmpark Posts: 1,305
    edited 2009-01-21 06:09
    mctrivia, I don't think res does what you think it does.

    William, what does "String (length of bytes 15-30)" mean?
  • jazzedjazzed Posts: 11,803
    edited 2009-01-21 06:28
    {{
    Example of a database and structure in Spin
    }}
     
    CON
    
      ' database entries
      DBSIZE = 3
     
    DAT
    
     
      ' database datastructure
      enumeration  long 0
      namestr      byte 0 [noparse][[/noparse]32] ' best to keep long alignment, but not necessary
      price        long 0
      end_mark     long 0
     
      ' database storage
      data         byte 0 [noparse][[/noparse](@end_mark-@enumeration)*DBSIZE]
     
     
    PUB main | n, enump, namep, pricep, recsize
     
      ' calculate record size for element access
      recsize := @end_mark-@enumeration+4
      
      repeat n from 0 to DBSIZE-1
     
        ' get pointers
        enump := @data+n*(recsize)  
        namep := [url=mailto:enump+@namestr-@enumeration]enump+@namestr-@enumeration[/url]
        pricep := [url=mailto:namep+@price-@namestr]namep+@price-@namestr[/url]
     
        ' assign some values
        long[noparse][[/noparse]enump] := n
        [s]namep := string("This is my record string")[/s]
        bytemove(namep,string("This is my record string",0),31)
        long[noparse][[/noparse]pricep] := -1
    
     
      repeat          
    

    Added: The pointers example just shows the math of access. Better design would be to have accessor methods to hide the details. Cheers.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    --Steve

    Post Edited (jazzed) : 1/22/2009 1:09:46 AM GMT
  • Paul BakerPaul Baker Posts: 6,351
    edited 2009-01-21 09:19
    If you have a static database you can entirely code it in·DAT as is, but I would use inherent enumeration and make it so the string is at the end:

    DAT
    recidx LONG @r1, @r2, @r3, @r4, 0 
    r1   LONG 124             'Value $1.24
         BYTE "Coke 20oz",0
    r2   LONG 526
         BYTE "Suave Shampoo",0
    r3   LONG 10900
         BYTE "GE Microwave",0
    r4   LONG 120000
         BYTE "Sanyo Plasma TV",0
     
     
    .... 'Spin code
    [s]recptr := LONG[noparse][[/noparse]@recidx][noparse][[/noparse] i ][/s] [b]correct code in later post, thanks mpark.[/b]
    if recptr <> 0
       price := LONG[noparse][[/noparse]recptr]
       strptr := recptr + 4
     
    

    After the code is run price contains the cost of the item i in pennies and strptr can be used·as if you were declaring string("...").

    You could extend this to be an expandable database by padding recidx with additional values of zero for whatever the maximum number of records you'll allow are ( ....., @r4, 0[noparse][[/noparse]MAX_ADDREC]), and at the end of defined records you would declare enough longs to encompass all the anticipated·records followed by·RECEND BYTE 0.
    When you add a record you iterate through recidx until you find a 0, then compare the location's address with r1 to make sure it is =< 8 bytes from r1 (room to add a record and still have a trailing 0L),·find the end of the last record by searching through the last record's string for a 0 byte. The next·long aligned address is the start of the new record, record this value in·open recidx slot you found and add the record info. But when you do this you must boundry check to make sure that the final location used is less than RECEND. If there isn't room for the index or the record you should not enter the record and exit gracefully.

    You can speed up entering records by using another variable NumRec that is the index of·the last used record (the space for this variable is reclaimed by not having·a trailing 0 in recidx), then use the next recidx entry to indicate what address·a new record would go.


    You didn't state if this was to be an exapandable or static database, if my instructions for an expandable database aren't clear enough for you to code yourself and thats what you need, I can whip it up for you.·

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Paul Baker


    Post Edited (Paul Baker) : 1/21/2009 10:53:12 PM GMT
  • Paul BakerPaul Baker Posts: 6,351
    edited 2009-01-21 09:36
    jazzed said...
     
    PUB main | n, enump, namep, pricep, recsize
     
      ' calculate record size for element access
      recsize := @end_mark-@enumeration+4
      
      repeat n from 0 to DBSIZE-1
     
        ' get pointers
        enump := @data+n*(recsize)  
        namep := [url=mailto:enump+@namestr-@enumeration]enump+@namestr-@enumeration[/url]
        pricep := [url=mailto:namep+@price-@namestr]namep+@price-@namestr[/url]
     
        ' assign some values
        long[noparse][[/noparse]enump] := n
        namep := string("This is my record string")
        long[noparse][[/noparse]pricep] := -1
    
     
      repeat          
    

    Added: The pointers example just shows the math of access. Better design would be to have accessor methods to hide the details. Cheers.

    namep := string("This is my record string")
    isnt doing what you think, it's assigning the address returned by string("... rather than copying the string itself, bytemove should be used to copy the contents. (you could use a pointer there, but your declaration of 32 bytes indicate this wasn't what you were attempting)

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Paul Baker
  • william chanwilliam chan Posts: 1,326
    edited 2009-01-21 09:58
    Paul,

    Your idea of inherent enumeration is ingenious!
    Where did you learn to code like that?

    I just don't quite understand this code

    recptr := LONG[noparse][[/noparse]@recidx][noparse][[/noparse] i ]

    where you can find a record's starting position so easily even with variable length strings.
    What's the meaning of LONG when used as a functor? ( i hope you know what i mean )

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    www.fd.com.my
    www.mercedes.com.my
  • Paul BakerPaul Baker Posts: 6,351
    edited 2009-01-21 11:13
    No problem, glad to help. Data structures have always been a natural talent for me, for some reason I can organize things in a logical fashion easily, got a 108 in my college data structures course (by doing all the extra credit availible).

    Basically recidx is an array of addresses that point to the beginning of each record, and is therefore easy to populate by just filling it with the addresses of the record labels. So the variableness of the records is hidden in this array by simply having each entry point to the right place. Another not so obvious thing is to put the fixed part of the record first so that you always know thier offset in the record. The first variable length field is also easily accessed because we always know the offset where it starts since everything preceeding it is fixed length. The ugliness starts when you have more than one variable length field (which you fortunately don't have).

    LONG[noparse][[/noparse]@recidx][noparse][[/noparse] i ] is treating hub memory as one huge array of longs, [noparse][[/noparse]@recidx] is the base, and [noparse][[/noparse] i ] is the offset, IOW starting at address recidx return the ith long (in other languages this would be recidx[noparse][[/noparse] i ] where recidx is declared as an array of longs). So this is a way of treating data in the DAT section as an array. Since what is stored in the array is an absolute address of the ith record, to get the price (the 1st long in the record) you use LONG[noparse][[/noparse]recptr] to fetch the value located there, if there was a second long value stored after the price it would be accessed with LONG[noparse][[/noparse]recptr][noparse][[/noparse] 1 ], or starting at address stored in recptr return the 2nd long (remember an array's first index is 0, so 1 is the 2nd long in the record).

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Paul Baker


    Post Edited (Paul Baker) : 1/21/2009 11:22:26 AM GMT
  • jazzedjazzed Posts: 11,803
    edited 2009-01-21 13:23
    Paul said: namep := string("This is my record string") isnt doing what you think ...

    Yep. I was tired.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    --Steve
  • mparkmpark Posts: 1,305
    edited 2009-01-21 17:17
    Paul Baker said...
    ... Since what is stored in the array is an absolute address of the ith record...

    But it isn't the absolute address, it's an object-relative address. You need to use @@ to get the absolute address:
    recptr := @@LONG[noparse][[/noparse]@recidx][noparse][[/noparse] i ]
  • Paul BakerPaul Baker Posts: 6,351
    edited 2009-01-21 21:09
    No, you're thinking too hard about it. Absolute addresses are provided to you when you use @label. @@ is almost never used and @@LONG is never used. @@ is for when there is no way for the compiler to compute the absolute address at compile time and the situations that its true is only true for one very rare case (Don't ask me to tell you what it is, I've asked Jeff at least 2 dozen times in the couple years I was with Parallax, and while the explanation made sense each time, I just couldn't keep it in my head. It's just one of the rare things I'm not meant to know off the top of my head, probably because I've never had to use it and can't envision a situation while doing project coding where I would).

    I stand corrected, read posts below.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Paul Baker


    Post Edited (Paul Baker) : 1/21/2009 10:46:00 PM GMT
  • mparkmpark Posts: 1,305
    edited 2009-01-21 21:18
    Paul, did you actually run your code?
  • Paul BakerPaul Baker Posts: 6,351
    edited 2009-01-21 21:24
    no, but i've coded similar things, why have you experienced issues with running it?

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Paul Baker
  • mparkmpark Posts: 1,305
    edited 2009-01-21 21:56
    Yes. I really think you should try it yourself.
  • Paul BakerPaul Baker Posts: 6,351
    edited 2009-01-21 22:35
    Ok when I get the chance to pull my gear out I'll look at it.

    Out of curiosity did your @@LONG[noparse][[/noparse].. work?

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Paul Baker
  • Paul BakerPaul Baker Posts: 6,351
    edited 2009-01-21 22:40
    Well what do you know, I think I stand corrected it looks like a @@ is needed somewhere, let me look at it further to confirm where it belongs.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Paul Baker
  • Paul BakerPaul Baker Posts: 6,351
    edited 2009-01-21 22:48
    Ok this is an updated version which should work.
    DAT
    recidx WORD @r1, @r2, @r3, @r4, 0 
    r1   LONG 124             'Value $1.24
         BYTE "Coke 20oz",0
    r2   LONG 526
         BYTE "Suave Shampoo",0
    r3   LONG 10900
         BYTE "GE Microwave",0
    r4   LONG 120000
         BYTE "Sanyo Plasma TV",0
     
     
    .... 'Spin code
    [b]recptr := @@recidx[noparse][[/noparse] i ][/b]
    if recptr <> 0
       price := LONG[noparse][[/noparse]recptr]
       strptr := recptr + 4
    
    

    This is the one time where @@ is needed, when you are recording·an address·in a DAT (compile time), since the objects haven't been linked yet there's no way to compute the final absolute address so the relative object address·is stored and the @@ operator indicates to the spin interpreter that it needs to add the object's base address to arrive at the absolute address.··Thanks for pointing out the error mpark.

    Turns out the compiler allows you to use a DAT label as an array. (LONG[noparse][[/noparse]label][noparse][[/noparse] i ] also works but it's more verbose).·

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Paul Baker


    Post Edited (Paul Baker) : 1/21/2009 11:02:29 PM GMT
  • Paul BakerPaul Baker Posts: 6,351
    edited 2009-01-21 23:03
    Aw crud this messes with the boundry checking since the 0 has the object address added to it. Lemmie tinker with it some more so this actually works.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Paul Baker
  • Paul BakerPaul Baker Posts: 6,351
    edited 2009-01-21 23:17
    DAT
    NumRec WORD 4
    recidx WORD @r1, @r2, @r3, @r4 
    r1   LONG 124             'Value $1.24
         BYTE "Coke 20oz",0
    r2   LONG 526
         BYTE "Suave Shampoo",0
    r3   LONG 10900
         BYTE "GE Microwave",0
    r4   LONG 120000
         BYTE "Sanyo Plasma TV",0
    f1   LONG 0[noparse][[/noparse]FreeRec]
     
     
    .... 'Spin code
    if i < NumRec
       recptr := @@recidx[noparse][[/noparse] i ]
       price := LONG[noparse][[/noparse]recptr]
       strptr := recptr + 4
    
    

    Ok, this uses an explicit record count to get around the issue created by using @@. The @@ also complicates dynamic records because at run time you're dealing with absolute addresses, so to enter a new value into recidx you will need to figure out how to subtract the abject base address so that the values can be reconstituted through @@ like the static records are.


    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Paul Baker
  • mparkmpark Posts: 1,305
    edited 2009-01-22 00:31
    If you want to still use the 0 sentinel value you could do something like this:
    DAT
    recidx WORD @r1, @r2, @r3, @r4, 0 
    ... 
     
    .... 'Spin code
    if [b]recidx[noparse][[/noparse] i ][/b] <> 0
    [b]   recptr := @@recidx[noparse][[/noparse] i ][/b]
       price := LONG[noparse][[/noparse]recptr]
       strptr := recptr + 4
    

    Also, @@0 gives you the object base address if you want to convert from absolute addresses to object-relative.
  • william chanwilliam chan Posts: 1,326
    edited 2009-01-22 00:42
    Why did you change the index size from LONG to WORD?
    Is it to save space?

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    www.fd.com.my
    www.mercedes.com.my
  • Paul BakerPaul Baker Posts: 6,351
    edited 2009-01-22 00:48
    william chan said...
    Why did you change the index size from LONG to WORD?
    Is it to save space?

    yes, the size of hub memory always fits into a word, so I decreased it to save some space.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Paul Baker
Sign In or Register to comment.