// ------------------------------- // // -------- Start of File -------- // // ------------------------------- // // ----------------------------------------------------------- // // C++ Source Code File Name: grocery.cpp // Compiler Used: MSVC, BCC32, GCC, HPUX aCC, SOLARIS CC // Produced By: glNET Software // File Creation Date: 09/18/1997 // Date Last Modified: 05/25/2001 // Copyright (c) 2001 glNET Software // ----------------------------------------------------------- // // ------------- Program Description and Details ------------- // // ----------------------------------------------------------- // /* This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA This is a test program use to test the Persistent base class. */ // ----------------------------------------------------------- // #include "grocery.h" GroceryKey::GroceryKey() : DatabaseKeyB((char *)&key) { for(int i = 0; i < MAX_NAME_LENGTH; i++) key.object_name[i] = 0; key.object_id = (gxObjectID)0; key.class_id = (gxClassID)0; } GroceryKey::GroceryKey(const char *name, gxObjectID oid, gxClassID cid) : DatabaseKeyB((char *)&key) { strncpy(key.object_name, name, MAX_NAME_LENGTH); key.object_name[MAX_NAME_LENGTH-1] = 0; // Ensure null termination key.object_id = oid; key.class_id = cid; } int GroceryKey::operator==(const DatabaseKeyB& k) const { const GroceryKey *kptr = (const GroceryKey *)(&k); int rv = strcmp(key.object_name, kptr->key.object_name); // NOTE: Duplicate names will not be allowed if the object ID is ignored return rv == 0; // Allow duplicate names by comparing the object ID // return ((rv == 0) && (key.object_id == kptr->key.object_id)); } int GroceryKey::operator>(const DatabaseKeyB& k) const { const GroceryKey *kptr = (const GroceryKey *)(&k); int rv = strcmp(key.object_name, kptr->key.object_name); // NOTE: Duplicate names will not be allowed if the object ID is ignored return rv > 0; // Allow duplicate names by comparing the object ID // return ((rv > 0) && (key.object_id > kptr->key.object_id)); } void GroceryKey::SetObjectName(const char *s) { strncpy(key.object_name, s, MAX_NAME_LENGTH); key.object_name[MAX_NAME_LENGTH-1] = 0; // Ensure null termination } void Grocery::ClearName() // Clears the name string. { if(name) delete name; // Assumes the name string was allocated dynamically name = null_name; } __UWORD__ Grocery::ObjectLength() { return sizeof(stock_number) + sizeof(price) + StringFileLength(name); } gxDatabaseError Grocery::Write() { gxObjectHeader oh; // Allocate a block in the data file for this object's data objectaddress = pod->OpenDataFile()->Alloc(ObjectLength() + sizeof(gxObjectHeader)); // Check for any allocation errors if(objectaddress == (FAU)0) { return pod->GetDataFileError(); } oh.ClassID = GetClassID(); oh.ObjectID = objectaddress; // Write the object header to the datafile if(WriteObjectHeader(oh)!= gxDBASE_NO_ERROR) { return pod->GetDataFileError(); } // Write the item name to the data file if(WriteString(name) != gxDBASE_NO_ERROR) { return pod->GetDataFileError(); } // Write the item's stock number to the data file if(pod->OpenDataFile()->Write(&stock_number, sizeof(stock_number)) != gxDBASE_NO_ERROR) { return pod->GetDataFileError(); } // Write the item's price to the data file if(pod->OpenDataFile()->Write(&price, sizeof(price)) != gxDBASE_NO_ERROR) { return pod->GetDataFileError(); } // Write a 32-bit CRC checksum for the object // NOTE: This step should only be preformed if the application // requires the use of persistent checksum values. pod->OpenDataFile()->WriteObjectChecksum(objectaddress); if(pod->GetDataFileError() != gxDBASE_NO_ERROR) { return pod->GetDataFileError(); } // Add the entry to the Index file if(UsingIndex()) { GroceryKey key((const char *)name, oh.ObjectID, oh.ClassID); GroceryKey compare_key; if(!AddKey(key, compare_key)) { return pod->GetIndexFileError(); } } return gxDBASE_NO_ERROR; } gxDatabaseError Grocery::Read(FAU object_address) { gxObjectHeader oh; // Optimize seeks during intervening reads pod->OpenDataFile()->SeekTo(object_address); if(pod->GetDataFileError() != gxDBASE_NO_ERROR) { return pod->GetDataFileError(); } if(ReadObjectHeader(oh, object_address) != gxDBASE_NO_ERROR) { return pod->GetDataFileError(); } // Incorrect object type // NOTE: This step should only be preformed if class ID values // are required by the application. if(oh.ClassID != GetClassID()) { return pod->SetDataFileError(gxDBASE_BAD_CLASS_ID); } // Read the object's name and check for errors Name_t nbuf = (Name_t)ReadString(); if(!nbuf) { return pod->GetDataFileError(); } else { name = nbuf; } // Read the object's stock number if(pod->OpenDataFile()->Read(&stock_number, sizeof(stock_number)) != gxDBASE_NO_ERROR) { return pod->GetDataFileError(); } // Read the object's price if(pod->OpenDataFile()->Read(&price, sizeof(price)) != gxDBASE_NO_ERROR) { return pod->GetDataFileError(); } objectaddress = object_address; return gxDBASE_NO_ERROR; } int Grocery::Find() { // Search the index file for this entry if(UsingIndex()) { GroceryKey key((const char *)this->name); GroceryKey compare_key; if(!FindKey(key, compare_key)) return 0; objectaddress = key.ObjectID(); return 1; // Found the index file entry } // If not using index file search the data file Grocery grocery; FAU oa; // Object Address gxBlockHeader gx; // Block Header gxObjectHeader oh; // Object Header FAU gxdfileEOF = pod->OpenDataFile()->GetEOF(); FAU addr = (FAU)0; addr = pod->OpenDataFile()->FindFirstBlock(addr); // Search the entire file if(addr == (FAU)0) return 0; // No database blocks found in file grocery.name = this->name; grocery.stock_number = this->stock_number; grocery.price = this->price; while(1) { if((addr + pod->OpenDataFile()->BlockHeaderSize()) >= gxdfileEOF) break; if(pod->OpenDataFile()->Read(&gx, sizeof(gxBlockHeader), addr) != gxDBASE_NO_ERROR) { return 0; } if(gx.block_check_word == gxCheckWord) { if((__SBYTE__)gx.block_status == gxNormalBlock) { oa = addr + pod->OpenDataFile()->BlockHeaderSize(); if(ReadObjectHeader(oh, oa) != gxDBASE_NO_ERROR) { return 0; } if(oh.ClassID == GetClassID()) { if(Read(oa) != gxDBASE_NO_ERROR) { return 0; } if(strcmp(name, grocery.name) == 0) { objectaddress = oa; return 1; // Found unique data member } } } addr = addr + gx.block_length; // Goto the next database block } else { addr++; // Keep searching until a valid database block is found } } // Reset the objects data this->name = grocery.name; this->stock_number = grocery.stock_number; this->price = grocery.price; return 0; // Could not find } int Grocery::Delete() { if(UsingIndex()) { GroceryKey key((const char *)this->name); GroceryKey compare_key; if(!FindKey(key, compare_key)) return 0; // Could not find the key objectaddress = key.ObjectID(); if(!DeleteObject(objectaddress)) return 0; // Could not delete the object if(!DeleteKey(key, compare_key)) return 0; // Could not delete the key return 1; // The index and data file entry was deleted } // If not using index file search the data file if(!Find()) return 0; // Could not find the data file entry if(DeleteObject(objectaddress)) return 0; // Could not delete the object // The object was deleted from the data file return 1; } int Grocery::CompareIndex(unsigned index_number) // Compares the data file to the index file. // Returns true if data and index file match. { if(!UsingIndex()) return 0; // Ensure that the in memory buffers and the file data // stay in sync during multiple file access. pod->Index(index_number)->TestTree(); Grocery grocery(pod); GroceryKey key, compare_key; FAU oa; // Object Address gxBlockHeader gx; // Block Header gxObjectHeader oh; // Object Header int objects = 0; // Keeps track of good database blocks int matches = 0; // Keep track of matches FAU gxdfileEOF = pod->OpenDataFile()->GetEOF(); FAU addr = (FAU)0; addr = pod->OpenDataFile()->FindFirstBlock(addr); // Search the entire file if(addr == (FAU)0) return 0; // No database blocks found in file while(1) { if((addr + pod->OpenDataFile()->BlockHeaderSize()) >= gxdfileEOF) break; pod->OpenDataFile()->Read(&gx, sizeof(gxBlockHeader), addr); if(gx.block_check_word == gxCheckWord) { if((__SBYTE__)gx.block_status == gxNormalBlock) { oa = addr + pod->OpenDataFile()->BlockHeaderSize(); ReadObjectHeader(oh, oa); if(oh.ClassID == GetClassID()) { objects++; // Increment the object count grocery.Read(oa); key.SetObjectName(grocery.name); key.SetObjectID(oa); key.SetClassID(oh.ClassID); if(FindKey(key, compare_key, index_number)) { matches++; // Index and data file match } grocery.ClearName(); } } addr = addr + gx.block_length; // Goto the next database block } else { addr++; // Keep searching until a valid database block is found } } return objects == matches; } int Grocery::RebuildIndexFile(const char *fname, unsigned index_number, int num_trees, BtreeNodeOrder_t node_order) { if(!UsingIndex()) return 0; GroceryKey key, compare_key; gxBtree btx(key, node_order); if(btx.Create(fname, num_trees) != gxDBASE_NO_ERROR) return 0; Grocery grocery(pod); FAU oa; // Object Address gxBlockHeader gx; // Block Header gxObjectHeader oh; // Object Header int objects = 0; // Keeps track of good database blocks int inserts = 0; // Keep track of inserts FAU gxdfileEOF = pod->OpenDataFile()->GetEOF(); FAU addr = (FAU)0; addr = pod->OpenDataFile()->FindFirstBlock(addr); // Search the entire file if(addr == (FAU)0) return 0; // No database blocks found in file while(1) { if((addr + pod->OpenDataFile()->BlockHeaderSize()) >= gxdfileEOF) break; pod->OpenDataFile()->Read(&gx, sizeof(gxBlockHeader), addr); if(gx.block_check_word == gxCheckWord) { if((__SBYTE__)gx.block_status == gxNormalBlock) { oa = addr + pod->OpenDataFile()->BlockHeaderSize(); ReadObjectHeader(oh, oa); if(oh.ClassID == GetClassID()) { objects++; // Increment the object count grocery.Read(oa); key.SetObjectName(grocery.name); key.SetObjectID(oa); key.SetClassID(oh.ClassID); grocery.ClearName(); // Prevent any memory leaks // Could not add the key if(!btx.Insert(key, compare_key)) return 0; inserts++; // Index and data file match } } addr = addr + gx.block_length; // Goto the next database block } else { addr++; // Keep searching until a valid database block is found } } return objects == inserts; } // ----------------------------------------------------------- // // ------------------------------- // // --------- End of File --------- // // ------------------------------- //