PDA

View Full Version : Creating a Database



william chan
01-21-2009, 09:31 AM
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 (http://www.fd.com.my)
www.mercedes.com.my (http://www.mercedes.com.my)

mctrivia
01-21-2009, 09:45 AM
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.

mpark
01-21-2009, 02:09 PM
mctrivia, I don't think res does what you think it does.

William, what does "String (length of bytes 15-30)" mean?

jazzed
01-21-2009, 02:28 PM
{{
Example of a database and structure in Spin
}}

CON

' database entries
DBSIZE = 3

DAT


' database datastructure
enumeration long 0
namestr byte 0 [32] ' best to keep long alignment, but not necessary
price long 0
end_mark long 0

' database storage
data byte 0 [(@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 := enump+@namestr-@enumeration (mailto:enump+@namestr-@enumeration)
pricep := namep+@price-@namestr (mailto:namep+@price-@namestr)

' assign some values
long[enump] := n
namep := string("This is my record string")
bytemove(namep,string("This is my record string",0),31)
long[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 Baker
01-21-2009, 05:19 PM
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
recptr := LONG[@recidx][ i ] correct code in later post, thanks mpark.
if recptr <> 0
price := LONG[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[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 (mailto:pbaker@parallax.com)


Post Edited (Paul Baker) : 1/21/2009 10:53:12 PM GMT

Paul Baker
01-21-2009, 05:36 PM
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 := enump+@namestr-@enumeration (mailto:enump+@namestr-@enumeration)
pricep := namep+@price-@namestr (mailto:namep+@price-@namestr)

' assign some values
long[enump] := n
namep := string("This is my record string")
long[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 (mailto:pbaker@parallax.com)

william chan
01-21-2009, 05:58 PM
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[@recidx][ 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 (http://www.fd.com.my)
www.mercedes.com.my (http://www.mercedes.com.my)

Paul Baker
01-21-2009, 07:13 PM
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[@recidx][ i ] is treating hub memory as one huge array of longs, [@recidx] is the base, and [ i ] is the offset, IOW starting at address recidx return the ith long (in other languages this would be recidx[ 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[recptr] to fetch the value located there, if there was a second long value stored after the price it would be accessed with LONG[recptr][ 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 (mailto:pbaker@parallax.com)


Post Edited (Paul Baker) : 1/21/2009 11:22:26 AM GMT

jazzed
01-21-2009, 09:23 PM
Paul said: namep := string("This is my record string") isnt doing what you think ...

Yep. I was tired.

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

mpark
01-22-2009, 01:17 AM
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[@recidx][ i ]

Paul Baker
01-22-2009, 05:09 AM
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 (mailto:pbaker@parallax.com)


Post Edited (Paul Baker) : 1/21/2009 10:46:00 PM GMT

mpark
01-22-2009, 05:18 AM
Paul, did you actually run your code?

Paul Baker
01-22-2009, 05:24 AM
no, but i've coded similar things, why have you experienced issues with running it?

▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Paul Baker (mailto:pbaker@parallax.com)

mpark
01-22-2009, 05:56 AM
Yes. I really think you should try it yourself.

Paul Baker
01-22-2009, 06:35 AM
Ok when I get the chance to pull my gear out I'll look at it.

Out of curiosity did your @@LONG[.. work?

▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Paul Baker (mailto:pbaker@parallax.com)

Paul Baker
01-22-2009, 06:40 AM
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 (mailto:pbaker@parallax.com)

Paul Baker
01-22-2009, 06:48 AM
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
recptr := @@recidx[ i ]
if recptr <> 0
price := LONG[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[label][ i ] also works but it's more verbose).·

▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Paul Baker (mailto:pbaker@parallax.com)


Post Edited (Paul Baker) : 1/21/2009 11:02:29 PM GMT

Paul Baker
01-22-2009, 07:03 AM
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 (mailto:pbaker@parallax.com)

Paul Baker
01-22-2009, 07:17 AM
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[FreeRec]


.... 'Spin code
if i < NumRec
recptr := @@recidx[ i ]
price := LONG[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 (mailto:pbaker@parallax.com)

mpark
01-22-2009, 08:31 AM
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 recidx[ i ] <> 0
recptr := @@recidx[ i ]
price := LONG[recptr]
strptr := recptr + 4

Also, @@0 gives you the object base address if you want to convert from absolute addresses to object-relative.

william chan
01-22-2009, 08:42 AM
Why did you change the index size from LONG to WORD?
Is it to save space?

▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
www.fd.com.my (http://www.fd.com.my)
www.mercedes.com.my (http://www.mercedes.com.my)

Paul Baker
01-22-2009, 08:48 AM
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 (mailto:pbaker@parallax.com)