PDA

View Full Version : [C] interfaccia per un gestore di memoria statica


noodles83
02-02-2008, 15:17
Mi è stato assegnato questo piccolo progetto da fare in C, ma ho alcuni dubbi che vorrei mi aiutaste a chiarirmi.

IL PROBLEMA:
Come parte dello sviluppo di una nuova consolle per videogiochi, siete stati incaricati di sviluppare il codice che si occuperà della
memorizzazione dei dati dei giochi (missioni svolte, livelli superati, opzioni di gioco, caratteristiche dei personaggi, ecc.). A causa di
limitazione dell'hardware, è disponibile per la memorizzazione di questi dati solo un banco di memoria statica di 64Kb. Tale memoria
viene gestita a pagine di 512 byte: ogni gioco eseguito sulla console (identificato da un codice identificativo unico, assegnato dal
produttore) può chiedere al gestore memoria di memorizzare un numero di pagine comprese fra 1 e 64 (ovvero, fra 512 e 32768
bytes). Nel caso non ci sia memoria disponibile nell'ambito dei 64Kb, il gestore deve abbandonare le pagine più vecchie fra quelle
memorizzate (si perdono cioè i dati memorizzati dal gioco che non è stato usato da più tempo per far spazio a quelli del gioco
attualmente in uso) e utilizzarle per soddisfare la richiesta corrente.

Si noti che tutti i dati di servizio necessari a gestire la memoria statica devono essere a loro volta conservati all'interno di tale
memoria (altrimenti, sarebbero persi allo spegnimento e successivo avvio della console); a tale scopo deve essere dedicato il minor
numero possibile di pagine, per lasciare maggior spazio ai dati dei giochi.


Mi devo costruire una API e va bene... ho pensato di usare un array bidimensionale per la memoria.

typedef unsigned char BYTE;
static BYTE pages[128][512];

Quello che mi turba, da nubbio, è la parte in corsivo... penso di non aver capito bene il problema. Dovrei salvare nella struttara dati che ho fatto tutte le variabili globali e quelle strutture ausiliare che eventualmente uso? E' questo il senso della cosa?

Mi continua però a sfuggire sempre qualcosa... primo... come è possibile fare una cosa del genere, per esempio salvare una struct in un array di byte?
secondo. perchè quello che inserisco in quell'array di byte dovrebbe essere preservato da un riavvio della console?

cionci
02-02-2008, 16:04
Perché hai usato la parola "static" nella dichiarazione ?

Quello scritto in corsivo significa che devi usare una o più pagine per andare a memorizzare i dati della tabella LRU (Least Recently Used) e la tabella della pagine occupate. Esistono diverse politiche più o meno efficienti e più o meno avide di memoria per gestire un algoritmo di rimpiazzamento LRU, ma spero che te le abbiano insegnate.

Ti consiglio di usare direttamente un vettore monodimensionale per la memoria. Ti dovrebbe rendere molto più semplice l'accesso per la scrittura e la lettura delle tabelle di cui sopra.

noodles83
02-02-2008, 16:14
Perché hai usato la parola "static" nella dichiarazione ?


perchè credo che non debba essere pubblica, no?

Come politica di rimpiazzamento avevo appunto in mente una cosa del genere, ma quello non è il mio problema.
Il mio problema indipendetemente dal tipo di politica utilizzata capire come poter memorizzare a loro volta queste tabelle o queste informazioni di rilocazione su l'array di byte.

cionci
02-02-2008, 16:24
perchè credo che non debba essere pubblica, no?
Forse per un progetto del genere è ammissibile.
Si non capisco però come poter memorizzare una tabella nell'array di byte... :confused:
Lavorando sugli indirizzi con i puntatori.
&memory[page * 512] ti ritorna l'indirizzo dell'inizio della pagina. Se io voglio lavorare a byte sulla pagina 1 posso tranquillamente fare:

char *lru = &memory[1]; /*indirizzo della pagina 1*/

Poi per accedere ai vari byte: lru[i] = 1;

Ovviamente sarai tu a doverti limitare sulla dimensione di lru e della tabella della memoria usata.

noodles83
02-02-2008, 16:39
scusa, avevo modificato il post precedente...

noodles83
04-02-2008, 10:29
magari sarò duro io... è molto probabile, ma il mio problema è un'altro.

Io ho questo array di byte, facciamolo mono-dimensionale, che in effetti è meglio.

Ho una struct così fatta:
typedef struct data{
long game_id; /* ID univoco del gioco*/
short block_id; /* ID del blocco*/
int pageSize; /* lunghezza in byte del blocco*/
}DATA;

se voglio salvare questa stuttura nell'array di byte, come faccio? devo usare memcpy()?

E come mi verrebbe "mappata" sull'array? fatemi un esempio per capire... mettiamo cosa una volta salvata voglia accedere al block_ID di questa struct.

cionci
04-02-2008, 10:49
BYTE memory[65536];

DATA *mytable;

//supponiamo di volerlo memorizzare nella seconda pagina
mytable = (DATA *)&memory[2*512];
//calcolo la quantità di strutture data che possono essere contenute nel blocco (anche se non serve, giusto per farti capire)
int mytable_size = 512 / sizeof(DATA);

mytable lo tratti esattamente come un normale vettore di DATA ed hai a disposizione mytable_size elementi per non sforare i limiti della pagina (cosa che probabilmente dovrai tenere di conto).

noodles83
04-02-2008, 11:44
BYTE memory[65536];

DATA *mytable;

//supponiamo di volerlo memorizzare nella seconda pagina
mytable = (DATA *)&memory[2*512];
//calcolo la quantità di strutture data che possono essere contenute nel blocco (anche se non serve, giusto per farti capire)
int mytable_size = 512 / sizeof(DATA);

mytable lo tratti esattamente come un normale vettore di DATA ed hai a disposizione mytable_size elementi per non sforare i limiti della pagina (cosa che probabilmente dovrai tenere di conto).


per prima cosa ti ringrazione per la pazienza e l'aiuto che mi stai dando.

mmm... vediamo se ho capito. In questo modo posso continuare ad accedere a memory byte a byte, ma nello stesso tempo usare mytable come se l'array fosse fatto di strutture DATA, operando però sulla stesso spazio di memoria. Giusto?

cionci
04-02-2008, 11:56
Sì...ed è la stessa cosa che si fa con malloc...infatti malloc ritorna un puntatore a void che viene poi castato al tipo di dato che ci interessa.
In pratica anche le tue API dovranno ritornare al gioco un puntatore a void ;)

PS: 2*512 è la terza pagina :stordita:

noodles83
04-02-2008, 12:01
Sì...ed è la stessa cosa che si fa con malloc...infatti malloc ritorna un puntatore a void che viene poi castato al tipo di dato che ci interessa.
In pratica anche le tue API dovranno ritornare al gioco un puntatore a void ;)

PS: 2*512 è la terza pagina :stordita:

perfetto. Inizio a vedere uno spiraglio di luce... :) Grazie.

Vedo nel pomeriggio di iniziare a buttare giù qualcosa di concreto.

noodles83
05-02-2008, 16:12
mi è venuto un dubbio...

se ho:
DATA* mytable=(DATA*)&memory[125];

se faccio:
mytable=mytable+1;

mytable punta all'area di memoria memory[126], cioè al byte successivo?

oppure mytable si sposta di una grandezza pari sizeof(DATA) ?

cionci
05-02-2008, 17:28
Si sposta di una grandezza pari a siezof(DATA).

marra11mg
06-02-2008, 16:23
ma le pagine che riservi per ogni gioco, possono essere in ordine sparso oppure basta che sono contigue?
In quest'ultimo caso sarebbe molto più semplice, potendo riservare 2kb alla tabella e usando gli altri 62k per salvare i giochi

noodles83
07-02-2008, 15:44
@marra11mg: possono essere anche i ordine sparso.

Vi chiedo ancora aiuto... ho un problema piccolo e uno grosso.

PROBLEMA PICCOLO:
vorrei sapere che warning è questo che mi viene sempre prodotto e non riesco ad eliminare?

In file included from memManager.c:8:
memManager.h:23: warning: no semicolon at end of struct or union

ne sapete qualcosa?


PROBLEMA GROSSO:
sto implementando questa funzione int psave(long game_id, short block_id, byte *buffer, int size);
vi riporto un pezzo di codice...

typedef unsigned char byte; /*posto in memManager.h*/
byte memory[65536]; /*è fuori dalla funzione, posta come variabile globale*/

/*nella funzione che sto implementando scrivo*/
mytable=(DATA*)&memory[0];
mytable->gioco=game_id;
mytable->blocco=block_id;
mytable->blocco_size=size+sizeof(DATA);
mytable->pages= n;
mytable->flag=1;
mytable->time=time(NULL);
memcpy(&memory[sizeof(DATA)],buffer,size); /*copio i dati in memoria*/

se provo a fare printf("%s", memory[sizeof(DATA)]) mi da segmentation fault.... come mai??? :help:

cionci
07-02-2008, 15:59
C'è scritto cosa significa.
Devi mettere un punto e virgolo dopo la parentesi chiusa della dichiarazione di una struct.

noodles83
07-02-2008, 16:00
C'è scritto cosa significa.
Devi mettere un punto e virgolo dopo la parentesi chiusa della dichiarazione di una struct.

c è il punto e virgola...

typedef struct data{

long gioco; /* ID univoco del gioco*/
short blocco; /* ID del blocco*/
int blocco_size;/* lunghezza in byte dei dati del blocco*/
int pages; /* numero di pagine occupate dal blocco*/
int flag; /* flag che indica con 0 se il blocco è libero, 1 altrimenti.*/
time_t time /* data dell'ultimo riferimento al blocco*/
} DATA;

noodles83
07-02-2008, 16:04
che imbecille!! mancava il punto e virgola alla variabile time.... :doh:

e per il problema grosso invece? c è una spiegazione?

cionci
07-02-2008, 16:15
@marra11mg: possono essere anche i ordine sparso.

Vi chiedo ancora aiuto... ho un problema piccolo e uno grosso.

PROBLEMA PICCOLO:
vorrei sapere che warning è questo che mi viene sempre prodotto e non riesco ad eliminare?

In file included from memManager.c:8:
memManager.h:23: warning: no semicolon at end of struct or union

ne sapete qualcosa?


PROBLEMA GROSSO:
sto implementando questa funzione int psave(long game_id, short block_id, byte *buffer, int size);
vi riporto un pezzo di codice...

typedef unsigned char byte; /*posto in memManager.h*/
byte memory[65536]; /*è fuori dalla funzione, posta come variabile globale*/

/*nella funzione che sto implementando scrivo*/
mytable=(DATA*)&memory[0];
mytable->gioco=game_id;
mytable->blocco=block_id;
mytable->blocco_size=size+sizeof(DATA);
mytable->pages= n;
mytable->flag=1;
mytable->time=time(NULL);
memcpy(&memory[sizeof(DATA)],buffer,size); /*copio i dati in memoria*/

se provo a fare printf("%s", memory[sizeof(DATA)]) mi da segmentation fault.... come mai??? :help:
memcpy(&memory[sizeof(DATA)],buffer,size); /*copio i dati in memoria*/

Che cosa vuoi fare con questa istruzione ? Perché indicizzi memory con sizeof(DATA) ?

printf("%s", memory[sizeof(DATA)]);

Che senso ha ? C'è una stringa a quell'indirizzo ? Se vuoi stampare una stringa devi usare &memory[sizeof(DATA)], se non c'è una stringa a quell'indirizzo allora proprio non ha senso quella stampa.
In memory[sizeof(DATA)] c'è il primo byte di buffer.

noodles83
07-02-2008, 16:27
memcpy(&memory[sizeof(DATA)],buffer,size); /*copio i dati in memoria*/

Che cosa vuoi fare con questa istruzione ? Perché indicizzi memory con sizeof(DATA) ?

in byte *buffer ho una stringa che voglio copiare in memoria a partire da memory[sizeof(DATA)]


printf("%s", memory[sizeof(DATA)]);

Che senso ha ? C'è una stringa a quell'indirizzo ? Se vuoi stampare una stringa devi usare &memory[sizeof(DATA)], se non c'è una stringa a quell'indirizzo allora proprio non ha senso quella stampa.
In memory[sizeof(DATA)] c'è il primo byte di buffer.

si voglio stamapare la stringa. però devo dire che in buffer ho inserito con un cliclo un carattere alla volta.

cionci
07-02-2008, 17:09
si voglio stamapare la stringa.
Allora devi usare la & davanti a memory. Spero che tu abbia copiato anche il carattere di finestringa.

noodles83
08-02-2008, 17:54
ennesimo problema... inizio ad impazzire! :muro:
La mia idea è quella di inserire nella memory i dati della struct necessari a gestire il blocco che sto inserendo seguiti dai dati "veri" (stringa di caratteri) che devono essere memorizzati.

Per il modo in cui ho deciso di gestire il tutto, all'inizio della prima pagina successiva al blocco salvato mi preparo già la struct per la gestione dell'eventuale blocco successivo. Quindi dopo la prima inserzione avrò praticamente la memoria divisa in due blocchi.

mytable=(DATA*)&memory[0];
mytable->gioco=game_id;
mytable->blocco=block_id;
mytable->blocco_size=size+sizeof(DATA);
mytable->pages= n;
mytable->flag=1;
mytable->time=time(NULL);
strncpy((char*)&memory[sizeof(DATA)],(char*)buffer,size);/*copio i dati in memoria*/
memory[sizeof(DATA)+size]=(byte)'\0';

freePages=128-n; /*aggiorno il numero di pagine della memoria ancora libere*/

/*preparo la struttura e lo spazio per il prossimo blocco*/


mytable=(DATA*)&memory[n*512];
mytable->gioco=0;
mytable->blocco=0;
mytable->blocco_size=0;
mytable->pages=freePages;
mytable->flag=0;
mytable->time=time(NULL);
spazioOK=TRUE; /*ho inserito tutto correttamente*/

In maniera del tutto semplificata questo è quello che faccio per esempio al primo salvataggio.
Mettiamo caso che ora devo fare un secondo salvataggio e che quindi devo accedere a quella struct che mi ero preparato per aggiornarla con i nuovi valori e ripetere praticamente il procedimento precedente.

/* (*i) è il primo byte della pagina*/

mytable->gioco=game_id;
mytable->blocco=block_id;
mytable->blocco_size=size+sizeof(DATA);
mytable->pages=n;
mytable->flag=1;
mytable->time=time(NULL);
strncpy((char*)&memory[(*i)+sizeof(DATA)],(char*)buffer,size);
memory[sizeof(DATA)+size]=(byte)'\0';
freePages=freePages-n;
spazioOK=TRUE;

come faccio questa operazione... sempre Segmentation Fault. Facendo vari test infatti non sovrascrive la stuttura che mi ero preparato precedentemente.
Dove è che sbaglio? :confused:

cionci
08-02-2008, 17:56
Imho non va bene la scelta che hai fatto. Devi creati un vettore di struct nelle prime pagine della memoria per gestire i descrittori delle pagine. Se fai come dici non assegni una pagina intera ad ogni gioco, ma solo 512 - la dimensione della struct.

noodles83
08-02-2008, 18:15
Imho non va bene la scelta che hai fatto. Devi creati un vettore di struct nelle prime pagine della memoria per gestire i descrittori delle pagine. Se fai come dici non assegni una pagina intera ad ogni gioco, ma solo 512 - la dimensione della struct.

si lo so, ma quello non è un problam perchè cmq gli assegno lo spazio di cui ha bisogno senza contare la dimensione del descrittore. Sicuramente non è una scelta ottima, però ormai ho impostato tutto cosi. Le API le ho giàò scritte e sono in fase di test.

Il primo inserimento funziona bene sono i successivi che non ne voglio sapere e non capisco il perchè? Non riesco a sovrascivvere ad accedere a quel descrittore "fasullo" che mi da segmentation fault.

cionci
08-02-2008, 18:26
Secondo me non va bene. Poi fai te :)

Guarda che quando hai a disposizione il puntatore al primo byte della pagina non devi più usare memory.

byte *i = &memory[n * 512];

Mettiamo che all'indirizzo i tu metta una struct data.
Quindi l'indirizzo del byte successivo alla struttura è i + sizeof(DATA).

noodles83
08-02-2008, 18:40
Secondo me non va bene. Poi fai te :)

Guarda che quando hai a disposizione il puntatore al primo byte della pagina non devi più usare memory.

byte *i = &memory[n * 512];

Mettiamo che all'indirizzo i tu metta una struct data.
Quindi l'indirizzo del byte successivo alla struttura è i + sizeof(DATA).


ehehe...sicuramente ho fatto una scelta infelice, ma purtroppo ormai ho solo il fine settimana per concludere con i test sperando che funzioni e non ho tempo di fare grossi cambiamenti, sono ad un passo dalla fine, spero di farcela.... se solo riuscissi a sistemare questa cosa che mi ha inchiodato tutto oggi... :muro:

se dovessi riuscire nell'impresa ovviamente ti dovrò offrire da bere! :D

marra11mg
08-02-2008, 18:41
sto provando a svolgere questo esercizio, appena trovo una soluzione accettabile te la posto

cionci
08-02-2008, 18:44
sto provando a svolgere questo esercizio, appena trovo una soluzione accettabile te la posto
Non mi sembra il caso...è bene che lo faccia da solo. Poi se dopo vogliamo discutere le possibili soluzioni a me va bene.

noodles83
08-02-2008, 18:58
ormai ne faccio una questione di principio... voglio vedere di arrivare in fondo, anche se ci credo poco... però ormai ci ho perso tanto tempo e non voglio mollare all'ultimo. Anche perchè ora vedere un'altra soluzione mi farebbe sicuramente andare fuori strada...

Caso mai come dice cionci, potermmo discutere la soluzione dopo... ora ho ancora 3gg per finire tutto.

noodles83
10-02-2008, 20:06
Finito!!

Domani scatta la consegna. Poi sarà il prof. a valutare...

Volevo ringraziare Cionci per il prezioso aiuto! Comunque vada sono contento di averlo finito. Grazie.:D

marra11mg
10-02-2008, 20:51
ottimo! Domani ti posto il listato di quello che avevo fatto io. Ciau e GL :)

marra11mg
11-02-2008, 12:07
Compilato con MS Visual C++ Express 9.0

NOTA: essendo il compilatore del linguaggio C++, ho utilizzato gli operatori ++ e NEW non disponibili nel C ANSI. Tuttavia al posto di new si può tranquillamente usare l'istruzione calloc(nElements, sizeof(elements)); stessa cosa vale per l'operatore ++

Intestazione e variabili globali



#define _CRT_SECURE_NO_DEPRECATE

#include <iostream>
#include <time.h>


typedef unsigned char BYTE;

struct MemoryMAP
{
long GUID; // GAME Global Unique IDentifier
BYTE StoreOrder; // ordine di salvataggio (se necessarie più pagine per un singolo salvataggio)
short sizeInsidePage; // dimensione dei dati all'interno della pagina (512 se pagina occupata completamente)
time_t tempo; // tempo di salvataggio
};



const int RAMSIZE = 65536; // dimensione memoria HARDWARE
const int pagSize = 512; // dimensione pagina di memoria
const int maxPageRequest = 64; // massimo numero di pagine consentite per gioco

// dimensione della memoria di servizio situata a inizio memoria HARDWARE (0 ~ 2047 bytes)
const int SERVICERAM_SIZE = RAMSIZE / pagSize * 16; // 16 è sizeof(MemoryMAP);

// totale pagine effettivamente disponibili per la memorizzazione dei dati dei giochi
const int TOTPAGES = (RAMSIZE - SERVICERAM_SIZE) / pagSize;


BYTE Memoria[RAMSIZE]; // 0 to 65535 MEMORIA DI SISTEMA (da 2048 a 65535 per i giochi salvati) i dati da 0 a 2047 sono riservati al sistema per indicizzare le altre pagine

// La struttura MemoryMAP, occupa 16 byte e ha la funzione di puntare alle pagine di memoria contenenti i dati
// veri e propri, nonchè a memorizzare tutti i parametri di ogni pagina (guid, age, store order ecc...)

// Dato che 16 * 128 fa 2048, l'area di memoria occupata dalle 128 MemoryMAP necessarie a gestire la Memoria sarà di
// 2 kilobytes (quindi la memoria effettivamente disponibile per i giochi sarà di 63488 byte o 124 su 128 pagine)

// a causa della presenza della memoria di servizio all'inizio della memoria HARDWARE e della conseguente riduzione
// della memoria per il salvataggio dei giochi, la parte di memoria di servizio che dovrebbe indirizzare le ultime
// 4 pagine di memoria HARDWARE, in realtà indirizzerebbe fuori dai confini dell'array Memoria.
// Di conseguenza, le pagine di memoria da 127 a 123 (in byte: da 2043 a 1983) resteranno sempre vuote e inutilizzate




Funzioni di servizio:


// funzioni di servizio

// restituisce un puntatore alla struttura che controlla la relativa area di memoria
MemoryMAP* GetMemoryIndexPage(BYTE pageIndex)
{
return ((MemoryMAP*) &Memoria[(pageIndex * sizeof(MemoryMAP))]);
}
// restituisce il puntatore al primo byte della pagina di memoria
BYTE* GetMemoryPage(BYTE pageIndex)
{
return &Memoria[(pageIndex * pagSize) + SERVICERAM_SIZE];
}
void HardwareMessage(char* message)
{
// è possibile utilizzare questa funzione per comunicare un evento all'hardware della console
// in questo caso viene utilizzata PRINTF per mostrare a video l'errore
printf("\n\nERROR: %s\n", message);
}


// restituisce il numero di pagine in uso da parte di un gioco
int PagesOwned(long GameGUID)
{
int count = 0;

MemoryMAP* mappa = GetMemoryIndexPage(0);

for (int i = 0; i < TOTPAGES; i++)
{
if (mappa->GUID == GameGUID) count++;
mappa++;
}

return count;
}

// cancella tutta la memoria (inclusa la tabella LRU)
void ClearMemory()
{
memset((void*)GetMemoryIndexPage(0), 0, RAMSIZE);
}

// restituisce il numero di pagine di memoria libera
int AvailableMem()
{
return PagesOwned(0); // 0 significa pagina vuota, nessun gioco può avere ID 0
}


// restituisce il numero di pagine necessarie al salvataggio in memoria
// dividendo la dimensione del vettore (void*) dati da salvare per la dimensione di una pagina
// se la divisione da resto, incrementa il numero di pagine necessarie di 1
long CalcMemoryRequest(long savedSize)
{
long nPages = savedSize / pagSize;
long reminder = savedSize % pagSize;

if (reminder) nPages++;

return nPages;
}

// restituisce un array di INDICI (non indirizzi fisici) di pagine di memoria vuote
BYTE* FindFreePages(long pagesRequested)
{
BYTE* ptrArray = new BYTE[pagesRequested];
long count = 0;
MemoryMAP* mappa = GetMemoryIndexPage(0);

for (BYTE i = 0; i < TOTPAGES; i++)
{
if (count == pagesRequested) break;

if (mappa[i].GUID == 0)
{
ptrArray[count] = i;
count++;
}
}

return ptrArray;
}





Funzioni di Salvataggio in memoria:



// memorizza fisicamente i dati in memoria
// Restituisce il tempo corrente alla funzione chiamante SaveGameData()
time_t StoreInMemory(long GameGUID, void* data, long savedSize, BYTE* pagPosition)
{
// numero di pagine da salvare
long addrsNumber = CalcMemoryRequest(savedSize);
// puntatore ai dati da salvare
BYTE* dataRaw = (BYTE*) data;

BYTE* currentPage = NULL;
MemoryMAP* mappa = NULL;


for (BYTE i = 0; i < addrsNumber; i++)
{
// assegno a mappa l'indirizzo della pagina in base all'indice
// i, pagPosition è l'array di indici di pagine vuote
mappa = GetMemoryIndexPage(pagPosition[i]);
// storeOrder indica l'ordine di memorizzazione dei blocchi da 512 byte ciascuno
mappa->StoreOrder = i;
mappa->GUID = GameGUID;
mappa->tempo = time(NULL);

// quando arrivo all'ultima pagina da salvare, immetto il valore corretto di savedSize
if (i == addrsNumber - 1)
mappa->sizeInsidePage = (short) (savedSize - ((addrsNumber - 1) * pagSize));
// altrimenti memorizzo 512 (pagina completamente in uso)
else
mappa->sizeInsidePage = pagSize;

// puntatore alla pagina di memoria fisica
currentPage = GetMemoryPage(pagPosition[i]);

// copio i dati sulla pagina di memoria fisica
memcpy((void*)currentPage, (void*)&dataRaw[i * pagSize], mappa->sizeInsidePage );

}
// cancello il puntatore di INDICI per non appesantire il programma
delete pagPosition; // free memory
return mappa->tempo;
}



// Gestisce il salvataggio dei giochi
// Restituisce il valore TEMPO del momento in cui sono stati salvati i dati
time_t SaveGameData(long GameGUID, void* savedGame, long savedSize)
{
time_t retVal = 0;

if (GameGUID == 0)
{
HardwareMessage("Invalid GUID provided");
return 0;
}


// Se la somma tra le pagine già possedute e quelle richieste da un gioco
// supera i 32k, invia un messaggio di errore
if ((PagesOwned(GameGUID) + CalcMemoryRequest(savedSize)) > maxPageRequest) // richiesta eccessiva
{
HardwareMessage("This game already owns the maximum available memory");
return 0;
}

// Finchè non ci sono pagine disponibili, cancella i dati meno utilizzati
while (AvailableMem() < CalcMemoryRequest(savedSize)) // memoria insufficiente
DeleteOlderPages();

// Chiamo la funzione che salva fisicamente i dati in memoria
// e gli passo: GUID del gioco, puntatore VOID ai dati, lunghezza in byte dei dati, array di INDICI delle pagine vuote.
retVal = StoreInMemory(GameGUID, savedGame, savedSize, FindFreePages(CalcMemoryRequest(savedSize)));

return retVal; // normal termination
}





Funzioni di cancellazione pagine non in uso:



// Cancella una pagina in base all'indice fornito
void ClearPage(BYTE pageIndex)
{
MemoryMAP* mappa = GetMemoryIndexPage(pageIndex);
BYTE* arrayDati = GetMemoryPage(pageIndex);

// cancello la struttura MemoryMAP dalla memoria
memset((void*)mappa, 0, sizeof(MemoryMAP));

// Questa istruzione è facoltativa, in quanto basterebbe cancellare la
struttura MemoryMAP che controlla la pagina fisica, per rendere quest'ultima, libera dal punto di vista del gestore di memoria

// cancello (pongo a zero) la pagina di memoria fisica
memset((void*) arrayDati, 0, pagSize);
}


// funzione che elimina le pagine più vecchie a seconda della richiesta di memoria
void DeleteOlderPages()
{
MemoryMAP* mappa = GetMemoryIndexPage(0);

// trova la o le pagine più vecchie

long GUID = 0;
time_t older = time(NULL);


// Primo ciclo: Determina qual'è il gioco più vecchio in memoria
for (BYTE i = 0; i < TOTPAGES; i++)
{
if (mappa[i].tempo == 0)continue; // salta pagine vuote

if (mappa[i].tempo < older)
{
older = mappa[i].tempo;
GUID = mappa[i].GUID;
}
}

// secondo ciclo: Cancella tutte le pagine del gioco più vecchio
for (BYTE i = 0; i < TOTPAGES; i++)
{
if ((mappa[i].tempo == older) && (mappa[i].GUID == GUID))
ClearPage(i);
}
}




Funzione di caricamento dati DA memoria:



// Algoritmo di ordinamento QUICKSORT
// Questa funzione viene utilizzata dalla funzione qsort() implementata
// nelle librerie standard del C++ per ordinare il vettore di indici SaveOrder
int Confronta( const void *arg1, const void *arg2 )
{
BYTE *indice1 = (BYTE*) arg1;
BYTE *indice2 = (BYTE*) arg2;
MemoryMAP* mappa1 = GetMemoryIndexPage(*indice1);
MemoryMAP* mappa2 = GetMemoryIndexPage(*indice2);

if (mappa1->StoreOrder < mappa2->StoreOrder) return -1;
else if (mappa1->StoreOrder == mappa2->StoreOrder) return 0;
else return 1;
}



// Restituisce un puntatore ai dati salvati in memoria
// Parametri necessari:
// 1) GUID del gioco
// 2) Tempo di quando è stato salvato il gioco
// 3) puntatore (in uscita) che conterrà la dimensione dei dati salvati

// NOTA: viene richiesto (e quindi verrà anche restituito dalla funzione di
// salvataggio) il tempo di quando è stato salvato il gioco, per evitare di
// dover chiedere all'utente, di scegliere tra più file salvati dallo stesso gioco
// in orari diversi.
// È altrimenti possibile modificare tale funzione è far scegliere all'utente quale
// dato caricare, ma ciò richiederebbe l'uso di printf() e scanf()

void* LoadGame(long GameGUID, time_t WhenSaved, long *SavedSize)
{
if (SavedSize == NULL)
{
HardwareMessage("Unable to load game data: parameter SavedSize was not provided to memory loader");
return NULL;
}

if (GameGUID == 0)
{
HardwareMessage("Unable to load game data: invalid Game GUID provided");
return NULL;
}

if (WhenSaved == 0)
{
HardwareMessage("Unable to load game data: invalid time reference provided to memory loader");
return NULL;
}


MemoryMAP* mappa = GetMemoryIndexPage(0);

BYTE countPages = 0; //contapagine corrispondenti alla richiesta (GUID e orario di salvataggio)

// conta tutte le pagine corrispondenti
for (BYTE i = 0; i < TOTPAGES; i++)
{
if ((mappa[i].GUID == GameGUID) && (mappa[i].tempo == WhenSaved)) countPages++;
}

if (countPages == 0)
{
HardwareMessage("Unable to load game data: Game GUID was not found or invalid TIME reference code");
return NULL;
}


// array di indici di pagine
BYTE* addressArray = new BYTE[countPages];
// variabile contenente la dimensione in byte dell'intero blocco (se multipagina)
long totSavedSize = 0;

// memorizza nell'array gli indici delle pagine
for (BYTE i = 0; i < TOTPAGES; i++)
{
if ((mappa[i].GUID == GameGUID) && (mappa[i].tempo == WhenSaved))
{
totSavedSize += mappa[i].sizeInsidePage;
addressArray[i] = i;
}
}

//ordina l'array in base al membro MemoryMAP.saveOrder
if (countPages > 1) qsort((void*)addressArray, countPages, sizeof(BYTE), Confronta);

// dati in uscita
BYTE* DATA_OUT = new BYTE[totSavedSize];

for (BYTE i = 0; i < countPages; i++)
{
memcpy((void*)&DATA_OUT[i * pagSize], (void*)GetMemoryPage(addressArray[i]), GetMemoryIndexPage(addressArray[i])->sizeInsidePage);
}

delete addressArray;

// aggiorno parametro in uscita
*SavedSize = totSavedSize;
return (void*) DATA_OUT;
}






Funzioni MAIN e di test del sistema: Queste funzioni non fanno parte del gestore di memoria, ma servono a testare il corretto funzionamento dell'applicazione



// funzioni esterne al gestore di memoria
// Funzione utilizzata solo per testare il funzionamento del programma
// In questo caso i dati vengono trattati come fossero stringhe
// Nel caso reale, i dati potrebbero essere di qualunque natura
void CaricaDaMemoria()
{
long gameGUID = 0;
char* DATI = NULL;
time_t tempo = 0;
long saveSize = 0;

printf("\n\nDigitare il GUID del gioco: ");
scanf("%d", &gameGUID);
printf("\nDigitare il codice TIME: ");
scanf("%d", &tempo);

if ((DATI = (char*) LoadGame(gameGUID, tempo, &saveSize)) == NULL)
return;

printf("\n%d bytes loaded from memory...", saveSize);

char* StringaOriginale = new char[saveSize + 1];

memcpy((void*)StringaOriginale, (void*) DATI, saveSize);
StringaOriginale[saveSize] = NULL; // ultimo carattere = NULL per renderla stampabile come "stringa" da printf()

printf("\n\nStringa Originale: %s\n", StringaOriginale);

delete StringaOriginale;
}

// funzione esterna al gestore di memoria
// Funzione di test, i dati utilizzati sono stringhe
void SalvaInMemoria()
{
long gameGUID = 0;
char stringBuffer[32768] = "";
char* ptrDati = NULL;

printf("\n\nDigitare il GUID del gioco: ");
scanf("%d", &gameGUID);
printf("\nDigitare una stringa di testo da salvare in memoria (senza spazi): ");
scanf("%s", stringBuffer);

/* debug purpose only
for (int i = 0; i < 300; i++)
stringBuffer[i] = 'a';

for (int i = 300; i < 620; i++)
stringBuffer[i] = 'z';

stringBuffer[620] = '\0'; */

ptrDati = new char[strlen(stringBuffer)];

memcpy((void*)ptrDati, &stringBuffer[0], strlen(stringBuffer)); // non uso strcpy per non copiare il byte \0

printf("Questo codice TIME ti servira' a prelevare il dato appena memorizzato in memoria: %d", SaveGameData(gameGUID, (void*)ptrDati, strlen(stringBuffer)));

delete ptrDati;
}


// funzione MAIN menu di selezione o console dei comandi
int main()
{
ClearMemory(); //reset hardware memory

int menu = 0; // variabile di menu

while (menu != 5)
{
printf("\n\n1) Salva dati in memoria");
printf("\n2) Carica dati da memoria");
printf("\n3) Visualizza memoria disponibile");
printf("\n4) Formatta memoria");
printf("\n5) Esci\n");
scanf("%d", &menu);

switch (menu)
{
case 1:
SalvaInMemoria();
break;
case 2:
CaricaDaMemoria();
break;
case 3:
printf("\n\nMemoria disponibile: %d bytes (%d pagine)\n", AvailableMem() * pagSize, AvailableMem());
break;
case 4:
ClearMemory();
break;
case 5:
break;
default:
printf("\n\n\nErrore, selezionare una voce di menu corretta\n");
}
}


printf("\n\n");
system("pause");
return 0; // normal termination
}




Infine, durante lo sviluppo ho avuto la necessità di crearmi delle funzioni di debug, per controllare che i dati in memoria fossero inseriti correttamente (ed evitare i fastidiosissimi segmentation fault :P )




void debugFunction_CheckMem()
{
system("cls");
printf("Memory Status: * = Occupied, - = Empty\n\n");

MemoryMAP* mappa = GetMemoryIndexPage(0);

for (long i = 0; i < TOTPAGES; i++)
{
if (mappa->GUID == 0) printf("-");
else printf("*");
mappa++;
}
}


void debugFunction_CheckControlPage(int pageN)
{
MemoryMAP* mappa = (MemoryMAP*) &Memoria[pageN * sizeof(MemoryMAP)];

printf("\n\nPage Address: %x", mappa);
printf("\nPage index: %d", pageN);
printf("\nGame GUID: %d", mappa->GUID);
printf("\nPage time: %d", mappa->tempo);
printf("\nPage saveorder: %d", mappa->StoreOrder);
printf("\nPage sizeData: %d\n", mappa->sizeInsidePage);

}