Quelques rappels à propos de la mémoire

La mémoire, une succession ordonnée d'octets
L'espace de travail
Accès aux données
Intervalle de travail
Statut de l'objet
Nettoyage
Redimensionner l'espace de travail
Libérer l'espace de travail
Pointer les données
Octet, série d'octets, bloc d'octets et série de blocs d'octets

 

 

 

 

 

Légende des schémas
N

Adresse de l'octet (en notation hexadécimale)

N

Adresse de début de la mémoire allouée (en notation hexadécimale)

N

Adresse intermédiaire de la mémoire allouée (en notation hexadécimale)

N

Adresse de fin de la mémoire allouée (en notation hexadécimale)

?

Contenu indéterminé/inconnu de l'octet

N
A

Contenu connu de l'octet (en notation hexadécimale) + valeur ASCII correspondante (sur seconde ligne)

N

Adresse de début de la mémoire allouée mais ne disposant d'aucun espace de travail (en notation hexadécimale)

N

Offset de l'octet par rapport à l'adresse de base (en notation hexadécimale)

 

 

 

 

 

La mémoire, une succession ordonnée d'octets

La mémoire est une zone constituée d'une série de cellules, appelée octets, dont le nombre total varie suivant le système sur laquelle elle est présente. Chacun de ces octets est disposé l'un à la suite de l'autre et porte un numéro d'identification incrémentiel propre et unique, on parle alors de son adresse. Chacun de ces octets est constitué de 8 bits qui peuvent contenir une information pouvant représenter un nombre s'étalant de 0 à 255 (soit du binaire 00000000 à 11111111).

Le schéma suivant montre une représentation simplifiée de la mémoire :

00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F
20 21 22 23 24 25 26 27 28 29 2A 2B 2C 2D 2E 2F
30 31 32 33 34 35 36 37 38 39 3A 3B 3C 3D 3E ..

Disparité entre les systèmes 32 bits et 64 bits

C'est le système qui accède à la mémoire. Il faut savoir que le système ne sait compter mécaniquement que jusqu'à ce que son architecture le lui permet. Les systèmes 32 bits ne peuvent compter que jusqu'à 4.294.967.295 (±4 Go) car 32 bits ne permettent pas de représenter un nombre supérieur et les systèmes 64 bits ne peuvent compter que jusqu'à 18.446.744.073.709.551.615 (±18 milliards de Go) car 64 bits ne permettent pas de représenter un nombre supérieur.

La librairie EclatLib est disponible en version 32 bits et 64 bits. Elle peut donc accéder à la totalité de la mémoire que se soit en 32 bits ou 64 bits.

 

 

 

 

 

L'espace de travail

L'espace de travail est la portion de la mémoire que le système nous a octroyé pour notre usage personnel. Pour disposer d'une partie de cette mémoire comme espace de travail, nous devons la réclamer au système, c'est l'allocation de mémoire.

Sur un système Windows 32 bits, la taille de mémoire allouable est (théoriquement) de ±4 Go (bien que celui-ci s'en réserve au mois 1/4 pour le noyau) soit 4.294.967.295 octets.

Sur un système Windows 64 bits, la taille de mémoire allouable est (théoriquement) de ±18 Go de Go (là aussi celui-ci s'en réserve une partie pour le noyau) soit 18.446.744.073.709.551.615 octets.

Allocation de mémoire

Le système cherche une série d'octets disponibles d'un seul tenant de la taille demandée. Une fois trouvée, il marque cette portion comme allouée pour ne pas qu'elle soit attribuée à d'autres programmes puis nous communique l'adresse du premier octet (appelée aussi adresse de base) de cette portion.

Description :

Instanciation d'un objet EMemory d'une taille de 20 octets d'espace de travail. La mémoire allouée a pour adresse de base 0x04. Toutes les données situées de l'adresse 0x04 à 0x17 font partie de l'espace de travail de l'objet EMemory.

Code :

EMemory eMem( 20 ); // objet EMemory alloué de 20 octets

eMem.Trace( _T("eMem") );

Sortie :

### Object EMemory 'eMem' Trace #
# Object address='0x0012F42C', status='EMemory::ObjectStatus_Enable' (code '3').
# Buffer address='0x00000004', workspace size='20' byte(s).
### End #

Vue :

00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E ..

Allocation impossible

En cas d'échec le système retourne l'adresse 0x00 (NULL).

Certes l'adresse 0x00 correspond au premier octet de la mémoire mais, par convention, elle est réservée pour spécifier une indisponibilité. Une adresse NULL est toujours synonyme d'adresse non valide.

Description :

Tentative d'allocation de 2 gigas de mémoire sur un système 32 bits. En mode débug, le message "HEAP[Dev.exe]: Invalid allocation size - 80000024 (exceeded 7ffdefff)" est affiché dans la fenêtre de sortie et aucun espace de travail n'est alloué (l'adresse de base est 0x00).

Code :

EMemory eMem( (ulonglong)1024L * 1024L * 1024L * 2 ); // objet EMemory alloué de 2 gigas d'octets (tentative)

eMem.Trace( _T("eMem") );

Sortie :

HEAP[Dev.exe]: Invalid allocation size - 80000024 (exceeded 7ffdefff)

### Object EMemory 'eMem' Trace #
# Object address='0x0012F468', status='EMemory::ObjectStatus_Free' (code '1').
# Buffer address='0x00000000', workspace size='0' byte(s).
### End #

Vue :

00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E ..

Pas de fragmentation

Il est important de comprendre que lorsque le système alloue de la mémoire, chacun des octets qui la compose doit et est toujours disposé l'un à la suite de l'autre.

Vous n'aurez jamais, contrairement aux fichiers, une mémoire allouée répartie sur plusieurs blocs de la mémoire, c'est à dire fragmentée. Si le système ne trouve pas la quantité de mémoire libre d'un seul bloc, il vous signifie que l'allocation a échoué.

Hors de l'espace de travail, point de salut

Il ne faut jamais effectuer de lecture ou d'écriture à l'extérieur de la zone de l'espace de travail sinon le système déclanchera une exception (erreur) qui fermera le programme sans possibilité de continuation.

L'écrasante majorité des bugs provient d'un accès non autorisé à la mémoire.

Particularité, allocation de 0 octet

Si la taille à allouer est égale à 0, alors la fonction alloue 0 octet et retourne erOk. C'est à dire que le pointeur m_pBuffer est valide (adresse différente de NULL) mais que l'espace de travail est vide. Cela semble curieux mais c'est la règle ! C'est un peu comme créer un fichier mais de ne rien y écrire dedant.

Cela n'est pas une erreur mais il est impossible d'effectuer une opération de lecture ou d'écriture sous peine d'erreur.

Description :

Instanciation d'un objet EMemory d'une taille de 0 octet d'espace de travail. La mémoire allouée a pour adresse de base 0x04.

Code :

EMemory eMem( 0 ); // objet EMemory alloué de 0 octets

eMem.Trace( _T("eMem") );

Sortie :

### Object EMemory 'eMem' Trace #
# Object address='0x0012F468', status='EMemory::ObjectStatus_Empty' (code '2').
# Buffer address='0x00000004', workspace size='0' byte(s).
### End #

Vue :

00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E ..

 

 

 

 

 

Accès aux données

Pour lire ou écrire une information dans l'un des octets de la mémoire allouée, il suffit d'utiliser son adresse.

Etant donné que les octets alloués sont TOUJOURS CONTINUS et CONTIGUS, il est simple de déterminer leur adresse. Le premier octet est situé à l'adresse de base, le second juste après, c'est à dire à l'adresse de base + 1 octet, le troisième à l'adresse de base + 2 octets, etc...

Cette indexation de l'adresse par rapport à l'adresse de base est appelé déplacement mais le plus souvent offset.

Adressage offsetisé

Chaque octet étant rangé l'un à la suite de l'autre, on calcule l'adresse de l'octet à manipuler en ajouter à l'adresse de base sa position par rapport à cette première par la formule suivante : Adresse de l'octet désiré = adresse de base + offset de l'octet désiré depuis l'adresse de base.

Ainsi pour le premier octet, l'adresse réelle est adresse de base + 0x00; pour le second octet, l'adresse réelle est adresse de base + 0x01; etc...

Description :

Instanciation d'un objet EMemory d'une taille de 4 octets d'espace de travail contentant les données 0x41, 0x42, 0x43 et 0x44 ("ABCD" en ASCII) avec lecture et affichage de ses données par la méthode d'un pointeur incrémentiel.

Puis on remplace chaque octet par les données respectives 0x45, 0x46, 0x47 et 0x47 ("EFGH" en ASCII) par adressage offsetisé.

Enfin on lit et affiche les données modifiées par opérateur offsetisé.

Code :

EMemory eMem( "ABCD", 4 ); // objet EMemory alloué de 4 octets
ulonglong ullCpt;
uchar *puc = eMem.PointerUCharGet(); // récupérer l'adresse de base

eMem.TraceEx( _T("eMem") );

for ( ullCpt=0; ullCpt<eMem.SizeGet(); ullCpt++ )
  {
  ETrace::DoEx( _T("L'octet %I64u se situe à l'offset %I64u, à l'adresse 0x%p et contient la donnée 0x%X ('%c').\n"),
                ullCpt,
                ullCpt,
                puc,
                *puc,
                *puc );
  puc++;
  }

ETrace::Do( _T("\n") );

puc = eMem.PointerUCharGet(); // récupérer l'adresse de base

*( puc + 0 ) = 'E';
*( puc + 1 ) = 'F';
*( puc + 2 ) = 'G';
*( puc + 3 ) = 'H';

eMem.TraceEx( _T("eMem") );

for ( ullCpt=0; ullCpt<eMem.SizeGet(); ullCpt++ )
  ETrace::DoEx( _T("L'octet %I64u se situe à l'offset %I64u, à l'adresse 0x%p et contient la donnée 0x%X ('%c').\n"),
                ullCpt,
                ullCpt,
                &eMem[ullCpt],
                eMem[ullCpt],
                eMem[ullCpt] );

Sortie :

### Object EMemory 'eMem' Trace #
# Object address='0x0012F468', status='EMemory::ObjectStatus_Enable' (code '3').
# Buffer address='0x00000004', workspace size='4' byte(s).
# Array data from offset '0x00000000' to offset '0x00000003', length=4 byte(s).
# Offset start 0x00000000 41 42 43 44 .. .. .. .. .. .. .. .. .. .. .. .. [ABCD]
### End #

L'octet 0 se situe à l'offset 0, à l'adresse 0x00000004 et contient la donnée 0x41 ('A').
L'octet 1 se situe à l'offset 1, à l'adresse 0x00000005 et contient la donnée 0x42 ('B').
L'octet 2 se situe à l'offset 2, à l'adresse 0x00000006 et contient la donnée 0x43 ('C').
L'octet 3 se situe à l'offset 3, à l'adresse 0x00000007 et contient la donnée 0x44 ('D').

### Object EMemory 'eMem' Trace #
# Object address='0x0012F468', status='EMemory::ObjectStatus_Enable' (code '3').
# Buffer address='0x00000004', workspace size='4' byte(s).
# Array data from offset '0x00000000' to offset '0x00000003', length=4 byte(s).
# Offset start 0x00000000 45 46 47 48 .. .. .. .. .. .. .. .. .. .. .. .. [EFGH]
### End #

L'octet 0 se situe à l'offset 0, à l'adresse 0x00000004 et contient la donnée 0x45 ('E').
L'octet 1 se situe à l'offset 1, à l'adresse 0x00000005 et contient la donnée 0x46 ('F').
L'octet 2 se situe à l'offset 2, à l'adresse 0x00000006 et contient la donnée 0x47 ('G').
L'octet 3 se situe à l'offset 3, à l'adresse 0x00000007 et contient la donnée 0x48 ('H').

Vue :

00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E ..
  00 01 02 03
41
A
42
B
43
C
44
D

00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E ..
  00 01 02 03
45
E
46
F
47
G
48
H

Limite d'accès

On ne peut accéder en lecture et/ou en écriture à une adresse qui ne fait pas partie intégrante de la mémoire allouée par votre programme. Le système renverrait alors une erreur qui le fermerait.

Dans l'exemple précédent, les adresses exploitables s'étendent de 0x04 à 0x07 inclus et par voie de conséquence, l'offset doit se situer entre la valeur 0x00 et 0x03.

Statut de l'offset

Suivant l'offset que vous utilisez, les fonctions membres de l'objet EMemory détermine son statut qui donnera lieu à l'exécution de celles-ci ou bien du renvoi prématuré d'une erreur.

Vous pouvez déterminer le statut d'un offset en utilisant la fonction OffsetStatusGet.

EMemory::OffsetStatus_Unknown

Le statut de l'offset n'a pu être déterminé. Ce statut ne devrait jamais apparaître, mais on n'est jamais assez prudent.

EMemory::OffsetStatus_In

L'offset est situé dans l'espace de travail.

00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E ..
00
+
01
+
02
+
03
+

EMemory::OffsetStatus_Limit

L'offset est situé à limite finale externe de l'espace de travail. Cette position ne fait pas partie de l'espace de travail et l'on ne peut effectuer d'opération directe à cette emplacement exepté les opérations d'insertion et de concaténation.

00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E ..
00
 
01
 
02
 
03
 
 
+

EMemory::OffsetStatus_Out

L'offset est situé au delà de la limite finale externe de l'espace de travail et de l'espace de travail. Il est interdit d'effectuer des opération directe (même d'insertion ou de concaténation).

00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E ..
 
+
 
+
 
+
 
+
00
 
01
 
02
 
03
 
 
+
 
+
 
+
 
+
 
+
 
+
 
..

 

 

 

 

 

Intervalle de travail

Avec l'objet EMemory vous avez accès à toute la mémoire que vous avez allouée, c'est l'espace de travail.

Il se peut que vous désiriez travailler, non pas sur tout l'espace de travail allouée mais que sur une portion; c'est l'intervalle de travail. L'intervalle de travail n'est pas une substitution à l'espace de travail mais un filtre additionnel. Ce n'est pas une particularité du C/C++ mais une fonctionnalité qui a été programmée pour étendre et simplifier la manipulation de données au sein de l'objet EMemory.

Lorsque vous utilisez un intervalle de travail, les manipulations n'ont lieu que sur l'intervalle de travail. Les données hors intervalle de travail ne le sont jamais.

Utiliser un intervalle de travail

Presque toutes les fonctions de l'objet EMemory supportent le travail sur un intervalle.

Ces fonctions sont reconnaissables par les deux derniers arguments Fonction( ..., ulonglong ullOffset = 0, ulonglong ullOffsetSize = EMemory::SizeUpToEnd );.

L'argument ulonglong ullOffset détermine le début de l'intervalle de travail, l'argument ulonglong ullOffsetSize détermine l'étendue de cet intervalle.

Par exemple si ullOffset est égal à 0x02 et que ullOffsetSize est égal à 0x03, la fonction utilisée ne travaillera que de l'offset 0x02 à l'offset 0x04 (et par voie de conséquence de l'adresse 0x06 à 0x08) :

00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E ..
  00
-
01
-
02
[+
03
+
04
+]
05
-
06
-
41
A
42
B
43
C
44
D
45
E
46
F
47
G

Maintenant si vous désirez définir un intervalle de travail démarrant d'un offset déterminé jusqu'à la fin de l'espace de travail, n'utilisez pas l'argument ullOffsetSize (ou plutôt laissez le à sa valeur par défaut pré-définie) ou donnez lui la valeur constante EMemory::SizeUpToEnd.

Lorsque vous utilisez la constante EMemory::SizeUpToEnd (pour l'argument ullOffsetSize) la fonction calcule automatiquement l'étendue de l'intervalle de travail pour qu'il aille jusqu'à la fin de l'espace de travail.

Par exemple si ullOffset est égal à 0x02 et que ullOffsetSize est égal à EMemory::SizeUpToEnd, la fonction utilisée ne travaillera que de l'offset 0x02 à l'offset 0x06 :

00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E ..
  00
-
01
-
02
[+
03
+
04
+
05
+
06
+]
41
A
42
B
43
C
44
D
45
E
46
F
47
G

Ne pas utiliser un intervalle de travail

Ne pas utiliser un intervalle de travail signifie travailler sur toutes l'étendue de l'espace de travail ou plutôt définir un intervalle de travail qui coïncide avec l'espace de travail.

Dans ce cas, il suffit de donner la valeur 0x00 à l'argument ullOffset et la valeur constante EMemory::SizeUpToEnd à ullOffsetSize.

Etant donné que ces arguments disposent de valeur par défaut pré-déterminée, il vous suffit de ne rien mettre et le compilateur affectera automatiquement la valeur 0x00 à ullOffset et EMemory::SizeUpToEnd à ullOffsetSize.

Par exemple si ullOffset est égal à 0x00 et que ullOffsetSize est égal à EMemory::SizeUpToEnd, la fonction utilisée travaillera sur toutes les octets de l'espace de travail :

00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E ..
  00
[+
01
+
02
+
03
+
04
+
05
+
06
+]
41
A
42
B
43
C
44
D
45
E
46
F
47
G

 

Description :

Instanciation d'un objet EMemory d'une taille de 4 octets d'espace de travail contenant les données 0x41, 0x42, 0x43 et 0x44 ("ABCD" en ASCII).

Puis remplissage de tous les octets de l'espace de travail avec la valeur 0x45 ("E" en ASCII).

Puis nous effectuons un remplissage avec la valeur 0x46 ("F" en ASCII) mais uniquement de l'intervalle s'étalant de l'offset 0x01 sur une étendue de 2 octets.

Enfin nous effectuons un remplissage avec la valeur 0x47 ("G" en ASCII) de l'offset 0x02 jusquà la fin de l'espace de travail, donc de l'intervalle s'étalant de l'offset 0x02 sur une étendue de 2 octets (cette étendue de 2 octets est auto-déterminé par la fonction appelée).

Code :

EMemory eMem( "ABCD", 4 ); // objet EMemory alloué de 4 octets

eMem.TraceEx( _T("eMem") );
eMem.ByteFill( 'E' ); // ou eMem.ByteFill( 'E', 0, EMemory::SizeUpToEnd );
eMem.TraceEx( _T("eMem") );
eMem.ByteFill( 'F', 1, 2 );
eMem.TraceEx( _T("eMem") );
eMem.ByteFill( 'G', 2 ); // ou eMem.ByteFill( 'G', 2, EMemory::SizeUpToEnd );
eMem.TraceEx( _T("eMem") );

Sortie :

### Object EMemory 'eMem' Trace #
# Object address='0x0012F468', status='EMemory::ObjectStatus_Enable' (code '3').
# Buffer address='0x00000004', workspace size='4' byte(s).
# Array data from offset '0x00000000' to offset '0x00000003', length=4 byte(s).
# Offset start 0x00000000 41 42 43 44 .. .. .. .. .. .. .. .. .. .. .. .. [ABCD]
### End #

### Object EMemory 'eMem' Trace #
# Object address='0x0012F468', status='EMemory::ObjectStatus_Enable' (code '3').
# Buffer address='0x00000004', workspace size='4' byte(s).
# Array data from offset '0x00000000' to offset '0x00000003', length=4 byte(s).
# Offset start 0x00000000 45 45 45 45 .. .. .. .. .. .. .. .. .. .. .. .. [EEEE]
### End #

### Object EMemory 'eMem' Trace #
# Object address='0x0012F468', status='EMemory::ObjectStatus_Enable' (code '3').
# Buffer address='0x00000004', workspace size='4' byte(s).
# Array data from offset '0x00000000' to offset '0x00000003', length=4 byte(s).
# Offset start 0x00000000 45 46 46 45 .. .. .. .. .. .. .. .. .. .. .. .. [EFFE]
### End #

### Object EMemory 'eMem' Trace #
# Object address='0x0012F468', status='EMemory::ObjectStatus_Enable' (code '3').
# Buffer address='0x00000004', workspace size='4' byte(s).
# Array data from offset '0x00000000' to offset '0x00000003', length=4 byte(s).
# Offset start 0x00000000 45 46 47 47 .. .. .. .. .. .. .. .. .. .. .. .. [EFGG]
### End #

Vue :

00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E ..
00 01 02 03
41
A
42
B
43
C
44
D

00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E ..
00
[+
01
+
02
+
03
+]
45
E
45
E
45
E
45
E

00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E ..
00
-
01
[+
02
+]
03
-
45
E
46
F
46
F
45
E

00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E ..
00
-
01
-
02
[+
03
+]
45
E
46
F
47
G
47
G

Statut d'un intervalle de travail

Suivant l'offset et son étendue que vous utilisez, les fonctions membres de l'objet EMemory détermine son statut qui donnera lieu à l'exécution de celles-ci ou bien du renvoi prématuré d'une erreur.

Vous pouvez déterminer le statut d'un intervalle en utilisant la fonction OffsetStatusSizeGet.

EMemory::OffsetStatus_Unknown

Le statut de l'intervallle n'a pu être déterminé. Ce statut ne devrait jamais apparaître, mais on n'est jamais assez prudent.

EMemory::OffsetStatus_In

L'intervalle est situé dans l'espace de travail.

00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E ..
00
[+
01
+
02
+
03
+]
o u [+ + +]
o u [+ + +]
o u [+]

EMemory::OffsetStatus_Limit

L'intervalle est situé à limite finale externe de l'espace de travail. Cet intervalle ne fait pas partie de l'espace de travail et il est interdit d'effectuer des opération directes (même d'insertion ou de concaténation).

00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E ..
00
 
01
 
02
 
03
 
 
[+
 
+
 
+
 
+
 
+
 
+
 
+
 
..

EMemory::OffsetStatus_Out

L'intervalle est situé au delà de la limite finale externe de l'espace de travail et de l'espace de travail. Il est interdit d'effectuer des opération directes (même d'insertion ou de concaténation).

00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E ..
 
[+
 
+
 
+
 
+]
00
 
01
 
02
 
03
 
 
[+
 
+
 
+
 
+
 
+
 
+
 
..

EMemory::OffsetStatus_Ride

L'intervalle est à cheval entre l'espace de travail et l'extérieur de l'espace de travail. L'offset est à l'intérieur mais une partie de son étendue à l'extérieure.

00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E ..
00
 
01
 
02
[+
03
+

+
 
+
 
+
 
+
 
+
 
+
 
+
 
..

 

Note : comme vous avez pu le constater les fonctions OffsetStatusGet et OffsetStatusSizeGet renvoie toutes les deux des constantes de type EMemory::OffsetStatus_.... Mais seule la fonction OffsetStatusSizeGet peut retourner la constante EMemory::OffsetStatus_Ride.

 

 

 

 

 

 

Statut de l'objet

Suivant l'état de l'objet, il a un statut spécifique qui autorise ou non certaines actions. L'objet se base sur ses deux variables membres protégées m_pBuffer et m_ullSize pour le déterminer.

La variable m_pBuffer est un pointeur de type void contenant l'adresse de base de l'espace de travail alloué et m_ullSize contient la taille de cet espace de travail.

Statut

m_pBuffer

m_ullSize

EMemory::ObjectStatus_Unknown

== NULL

> 0

EMemory::ObjectStatus_Free

== NULL

== 0

EMemory::ObjectStatus_Empty

> NULL

== 0

EMemory::ObjectStatus_Enable

> NULL

> 0

Vous pouvez déterminer le statut de l'objet en utilisant la fonction ObjetStatusGet.

EMemory::ObjectStatus_Unknown

Ce statut ne devrait jamais apparaître car les variables membres sont verrouillées. L'état des deux variables membres ne sont pas compatibles.

EMemory::ObjectStatus_Free

C'est le statut par défaut lorsque l'on crée un objet EMemory en appelant le constucteur par défaut. L'objet n'a aucun espace de travail.

00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E ..

EMemory::ObjectStatus_Empty

C'est le statut lorsque l'espace de travail est alloué mais de 0 octet. L'objet a un espace de travail mais il est vide d'octets (c'est comme pour un fichier qui existe mais qui aurait une taille de 0 octet).

00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E ..

EMemory::ObjectStatus_Enable

C'est le statut lorsque l'espace de travail est alloué avec au moins 1 octet. L'objet a un espace de travail et des données.

00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E ..

 

 

 

 

 

Nettoyage

Une fois notre espace de travail alloué, nous avons à notre disposition une série d'octets. Comme c'est toujours le cas, des informations sont déjà présentes dans chacun de ces octets. Ces données sont indéterminées, il s'agit de résidu d'une allocation tierce précédente de votre programme ou d'un autre.

Lorsque le système alloue ou désalloue de la mémoire, il ne nettoie pas les informations contenues dans chaque octet, elles restent telles quelles.

Description :

Instanciation d'un objet EMemory d'une taille de 4 octets d'espace de travail.

Code :

EMemory eMem( 4 ); // objet EMemory alloué de 4 octets

eMem.TraceEx( _T("eMem") );

Sortie :

### Object EMemory 'eMem' Trace #
# Object address='0x0012F468', status='EMemory::ObjectStatus_Enable' (code '3').
# Buffer address='0x00000004', workspace size='4' byte(s).
# Array data from offset '0x00000000' to offset '0x00000003', length=4 byte(s).
# Offset start 0x00000000 C0 C8 94 E9 .. .. .. .. .. .. .. .. .. .. .. .. [....]
### End #

Vue :

00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E ..
  00 01 02 03
C0
?
C8
?
94
?
E9
?

Comme vous pouvez le constater les données contenues dans chacun des octets alloués sont arbitraires. Le nettoyage n'est pas obligatoire mais conseillé pour eviter toutes méprises lors d'une utilisation future.

La convention veut que ce nettoyage se fasse avec la 0x00 mais dans les exemples donnés dans cette documentation j'utilise des valeurs plus perceptibles pour plus de relief.

Nettoyage dès l'instanciation

Dès que vous instanciez un objet EMemory, vous pouvez le nettoyer dans la foulée avec une valeur.

Description :

Instanciation d'un objet EMemory d'une taille de 4 octets d'espace de travail avec nettoyage avec la valeur 0x41 ("A" en ASCII)

Code :

EMemory eMem( 4, 'A' ); // objet EMemory alloué de 4 octets

eMem.TraceEx( _T("eMem") );

Sortie :

### Object EMemory 'eMem' Trace #
# Object address='0x0012F468', status='EMemory::ObjectStatus_Enable' (code '3').
# Buffer address='0x00000004', workspace size='4' byte(s).
# Array data from offset '0x00000000' to offset '0x00000003', length=4 byte(s).
# Offset start 0x00000000 41 41 41 41 .. .. .. .. .. .. .. .. .. .. .. .. [AAAA]
### End #

Vue :

00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E ..
  00 01 02 03
41
A
41
A
41
A
41
A

Nettoyage a posteriori

C'est à dire qu'il a lieu après que les octets aient été alloués.

Description :

Instanciation d'un objet EMemory d'une taille de 4 octets d'espace de travail, puis nettoyage postérieur avec la valeur 0x41 ("A" en ASCII)

Code :

EMemory eMem( 4 ); // objet EMemory alloué de 4 octets

eMem.TraceEx( _T("eMem") );
eMem.ByteFill( 'A' );
eMem.TraceEx( _T("eMem") );

Sortie :

### Object EMemory 'eMem' Trace #
# Object address='0x0012F468', status='EMemory::ObjectStatus_Enable' (code '3').
# Buffer address='0x00000004', workspace size='4' byte(s).
# Array data from offset '0x00000000' to offset '0x00000003', length=4 byte(s).
# Offset start 0x00000000 C0 C8 94 E9 .. .. .. .. .. .. .. .. .. .. .. .. [....]
### End #

### Object EMemory 'eMem' Trace #
# Object address='0x0012F468', status='EMemory::ObjectStatus_Enable' (code '3').
# Buffer address='0x00000004', workspace size='4' byte(s).
# Array data from offset '0x00000000' to offset '0x00000003', length=4 byte(s).
# Offset start 0x00000000 41 41 41 41 .. .. .. .. .. .. .. .. .. .. .. .. [AAAA]
### End #

Vue :

00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E ..
  00 01 02 03
C0
?
C8
?
94
?
E9
?

00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E ..
  00 01 02 03
41
A
41
A
41
A
41
A

Nettoyage a priori

C'est à dire qu'il a lieu avant que les octets ne soient librérés. Cette approche s'inscrit dans la volonté d'effacer toutes traces des données sensibles de la mémoire après librération de la mémoire qui les contenaient.

Description :

Instanciation d'un objet EMemory d'une taille de 4 octets d'espace de travail, puis on y stocke le mot de passe "ABCD".

Avant de libérer l'espace de travail, on écrase le mot de passe pour qu'il ne puisse être lu.

Code :

EMemory eMem( 4 ); // objet EMemory alloué de 4 octets

eMem.TraceEx( _T("eMem") );
eMem.BlockCopy( "ABCD", 4 );
eMem.TraceEx( _T("eMem") );
eMem.ByteFill( 0x00 );
eMem.TraceEx( _T("eMem") );
eMem.Free();
eMem.TraceEx( _T("eMem") );

Sortie :

### Object EMemory 'eMem' Trace #
# Object address='0x0012F468', status='EMemory::ObjectStatus_Enable' (code '3').
# Buffer address='0x00000004', workspace size='4' byte(s).
# Array data from offset '0x00000000' to offset '0x00000003', length=4 byte(s).
# Offset start 0x00000000 C0 C8 94 E9 .. .. .. .. .. .. .. .. .. .. .. .. [....]
### End #

### Object EMemory 'eMem' Trace #
# Object address='0x0012F468', status='EMemory::ObjectStatus_Enable' (code '3').
# Buffer address='0x00000004', workspace size='4' byte(s).
# Array data from offset '0x00000000' to offset '0x00000003', length=4 byte(s).
# Offset start 0x00000000 41 42 43 44 .. .. .. .. .. .. .. .. .. .. .. .. [ABCD]
### End #

### Object EMemory 'eMem' Trace #
# Object address='0x0012F468', status='EMemory::ObjectStatus_Enable' (code '3').
# Buffer address='0x00000004', workspace size='4' byte(s).
# Array data from offset '0x00000000' to offset '0x00000003', length=4 byte(s).
# Offset start 0x00000000 00 00 00 00 .. .. .. .. .. .. .. .. .. .. .. .. [....]
### End #

### Object EMemory 'eMem' Trace #
# Object address='0x0012F468', status='EMemory::ObjectStatus_Free' (code '1').
# Buffer address='0x00000000', workspace size='0' byte(s).
# No data to show from offset '0x00000000' to offset '0x00000000', length=0 byte(s).
### End #

Vue :

00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E ..
  00 01 02 03
C0
?
C8
?
94
?
E9
?

00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E ..
  00 01 02 03
41
A
42
B
43
C
44
D

00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E ..
  00 01 02 03
00 00 00 00

00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E ..
 
00 00 00 00

Note : Si l'on avait pas fait de nettoyage a priori le mot de passe serait toujours en mémoire :

00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E ..
 
41
A
42
B
43
C
44
D

 

 

 

 

 

Redimensionner l'espace de travail

Lorsque que l'on désire disposer de plus ou moins de mémoire, il est possible de redimensionner la quantité de mémoire allouée dans notre espace de travail. C'est la reallocation.

Note importante : lorsque le redimensionnement de l'espace de travail échoue (parce qu'il n'y a pas assez de mémoire par exemple), l'ancien espace de travail reste intact, il n'est pas détruit contrairement à ce que l'on pourrait penser. Par exemple si l'espace de travail était de 100 kilo-octets, que le redimensionnement demandé est de 1 Mo et que cela échoue, votre espace est toujours de 100 kilo-octets (les données sont les mêmes d'avant la demande de redimensionnement).

La reallocation de mémoire n'est pas la désallocation de mémoire allouée puis l'allocation d'une nouvelle mémoire dans la foulée. C'est un autre mécanisme. Le système va tenter conserver le même emplacement des octets actuellement alloués mais en modulant son étendu.

Pourquoi conserver le même emplacement ?

Le mécanisme de reallocation dans un autre emplacement est lourd. Une reallocation n'est qu'un redimensionnement. Cela implique que les informations contenues dans les informations contenues dans chaque octet de notre espace de travail actuel doivent subsister dans notre nouvel espace de travail. Si le système n'arrive pas à re-utiliser le même emplacement par manque de place à la queue de l'espace de travail actuel, il alloue un nouvel espace de travail, y copie les informations de l'ancien espace de travail (par contre les octets ajoutés à l'espace de travail ne sont pas nettoyés). L'ancien espace de travail est alors désalloué et l'adresse du premier octet du nouvel espace de travail est alors différente.

Description :

Instanciation d'un objet EMemory (eMem0) d'une taille de 4 octets d'espace de travail et d'un objet EMemory de 5 octets (eMem1).

Puis l'on redimensionne à la hausse l'objet eMem0 de 3 octets. Mais n'ayant pas assez de place à la suite de l'espace de travail le système déplace toutes les données existantes dans un nouvel emplacement.

Code :

EMemory eMem0( 4 ); // objet EMemory alloué de 4 octets
EMemory eMem1( 5 ); // objet EMemory alloué de 5 octets

eMem0.TraceEx( _T("eMem0") );
eMem1.TraceEx( _T("eMem1") );

eMem0.SizeSet( 7 );
eMem0.TraceEx( _T("eMem0") );

Sortie :

### Object EMemory 'eMem0' Trace #
# Object address='0x0012F468', status='EMemory::ObjectStatus_Enable' (code '3').
# Buffer address='0x00000004', workspace size='4' byte(s).
# Array data from offset '0x00000000' to offset '0x00000003', length=4 byte(s).
# Offset start 0x00000000 41 41 41 41 .. .. .. .. .. .. .. .. .. .. .. .. [AAAA]
### End #

### Object EMemory 'eMem1' Trace #
# Object address='0x0012F450', status='EMemory::ObjectStatus_Enable' (code '3').
# Buffer address='0x0000000A', workspace size='5' byte(s).
# Array data from offset '0x00000000' to offset '0x00000004', length=5 byte(s).
# Offset start 0x00000000 42 42 42 42 42 .. .. .. .. .. .. .. .. .. .. .. [BBBBB]
### End #

### Object EMemory 'eMem0' Trace #
# Object address='0x0012F468', status='EMemory::ObjectStatus_Enable' (code '3').
# Buffer address='0x00000010', workspace size='7' byte(s).
# Array data from offset '0x00000000' to offset '0x00000006', length=7 byte(s).
# Offset start 0x00000000 41 41 41 41 C0 C8 94 .. .. .. .. .. .. .. .. .. [AAAA...]
### End #

Vue :

00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E ..
00 01 02 03 00 01 02 03 04
41
A
41
A
41
A
41
A
42
B
42
B
42
B
42
B
42
B

Il n'a pas assez de mémoire disponible à la suite de l'objet eMem0 (2 octets de libres alors qu'il nous en faut 3) alors le système alloue une nouvelle plage mémoire à l'adresse 0x10 (de 7 octets) et y copie les données situées à l'adresse 0x04 :

00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E FF
00 01 02 03 00 01 02 03 04
41
A
41
A
41
A
41
A
42
B
42
B
42
B
42
B
42
B
10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E ..
00 01 02 03 04 05 06
41
A
41
A
41
A
41
A
C0
?
C8
?
94
?

Et l'ancien emplacement de l'objet eMem0 est libérée. L'objet a maintenant l'adresse 0x10 pour adresse de base de son espace de travail :

00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E FF
00 01 02 03 04
41
A
41
A
41
A
41
A
42
B
42
B
42
B
42
B
42
B
10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E ..
00 01 02 03 04 05 06
41
A
41
A
41
A
41
A
C0
?
C8
?
94
?

Vous aurez remarqué que les octets alloués en plus à l'objet eMem0 ne sont pas nettoyés, si vous le désirez utilisez les fonctions surchargées SizeSet( ulonglong ullNewSize, uchar ucByteToFill ) ou SizeSet( ulonglong ullNewSize, void *pBlockToFill, ulonglong ullBlockToFillSize ) pour y remédier.

Tout ça pour dire quoi ? Comme vous pouvez le voir le redimensionnement à la hausse peut être lourd, ne soyez pas petit bras : essayer d'allouer un espace de travail assez grand pour ne pas à avoir à faire de multiples redimensionnements qui peuvent s'avérer lourds suivant les cas.

Redimensionnement à la baisse

Les derniers octets faisant partie de l'espace de travail sont retirés.

Description :

Instanciation d'un objet EMemory d'une taille de 4 octets d'espace de travail contenant les données 0x41, 0x42, 0x43 et 0x44 ("ABCD" en ASCII).

Puis l'on redimensionne à la baisse de 3 octets avec nettoyage des octets supprimés à l'aide de la valeur 0x45 ("E" en ASCII).

Code :

EMemory eMem( "ABCD", 4 ); // objet EMemory alloué de 4 octets

eMem.TraceEx( _T("eMem") );
eMem.SizeSet( 1, 'E' );
eMem.TraceEx( _T("eMem") );

Sortie :

### Object EMemory 'eMem' Trace #
# Object address='0x0012F468', status='EMemory::ObjectStatus_Enable' (code '3').
# Buffer address='0x00000004', workspace size='4' byte(s).
# Array data from offset '0x00000000' to offset '0x00000003', length=4 byte(s).
# Offset start 0x00000000 41 42 43 44 .. .. .. .. .. .. .. .. .. .. .. .. [ABCD]
### End #

### Object EMemory 'eMem' Trace #
# Object address='0x0012F468', status='EMemory::ObjectStatus_Enable' (code '3').
# Buffer address='0x00000004', workspace size='1' byte(s).
# Array data from offset '0x00000000' to offset '0x00000000', length=1 byte(s).
# Offset start 0x00000000 41 .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. [A]
### End #

Vue :

00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E ..
00 01 02 03
41
A
42
B
43
C
44
D

La fonction SizeSet nettoie les octets qui vont être supprimés par la valeur 0x45 :

00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E ..
00 01 02 03
41
A
45
E
45
E
45
E

Puis elle libère les octets ne faisant plus partie de l'espace de travail :

00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E ..
00
41
A
45
E
45
E
45
E

Redimensionnement à la hausse

La reallocation à la hausse est différente car il faut qu'il y ait assez d'octets libres à la fin de l'espace de travail pour pouvoir conserver le même emplacement.

Description :

Instanciation d'un objet EMemory d'une taille de 4 octets d'espace de travail contenant les données 0x41, 0x42, 0x43 et 0x44 ("ABCD" en ASCII).

Puis l'on redimensionne à la baisse de 3 octets avec nettoyage des octets supprimés à l'aide de la valeur 0x45 ("E" en ASCII).

Code :

EMemory eMem( "ABCD", 4 ); // objet EMemory alloué de 4 octets

eMem.TraceEx( _T("eMem") );
eMem.SizeSet( 7, 'E' );
eMem.TraceEx( _T("eMem") );

Sortie :

### Object EMemory 'eMem' Trace #
# Object address='0x0012F468', status='EMemory::ObjectStatus_Enable' (code '3').
# Buffer address='0x00000004', workspace size='4' byte(s).
# Array data from offset '0x00000000' to offset '0x00000003', length=4 byte(s).
# Offset start 0x00000000 41 42 43 44 .. .. .. .. .. .. .. .. .. .. .. .. [ABCD]
### End #

### Object EMemory 'eMem' Trace #
# Object address='0x0012F468', status='EMemory::ObjectStatus_Enable' (code '3').
# Buffer address='0x00000004', workspace size='7' byte(s).
# Array data from offset '0x00000000' to offset '0x00000006', length=7 byte(s).
# Offset start 0x00000000 41 42 43 44 45 45 45 .. .. .. .. .. .. .. .. .. [ABCDEEE]
### End #

Vue :

00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E ..
00 01 02 03
41
A
42
B
43
C
44
D

00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E ..
00 01 02 03 04 05 06
41
A
42
B
43
C
44
D
45
E
45
E
45
E

Note à propos du nettoyage lors d'un redimensionnement

Tout d'abord il n'est obligatoire mais conseillé (à la hausse en tout cas); les fonctions de redimensionnement surchargées SizeSet( ulonglong ullNewSize, uchar ucByteToFill ) et SizeSet( ulonglong ullNewSize, void *pBlockToFill, ulonglong ullBlockToFillSize ) ont été spécialement écrites pour cela. Et vous n'aurez pas à le faire par la suite.

D'autre part, si la plage mémoire est déplacée au cours d'un redimensionnement à la hausse car il n'y a pas assez d'octets libre à la queue de l'espace de travail, il est impossible de nettoyer l'ancienne plage car il est impossible de savoir si la plage mémoire va être déplacée dans un autre emplacement à moins de mettre en place une procédure très lourde (ce qui n'est pas le cas dans l'objet EMemory).

 

 

 

 

 

Libérer l'espace de travail

Une fois que l'on a plus besoin de la mémoire allouée, il convient de la libérer pour qu'elle soit de nouveau disponible pour le système.

Lorsque l'objet est détruit, celui-ci libère automatiquement l'espace de travail alloué, si ce n'est déjà fait.

Description :

Instanciation d'un objet EMemory d'une taille de 4 octets d'espace de travail, puis libération.

Code :

EMemory eMem( 4 ); // objet EMemory alloué de 4 octets

eMem.Trace( _T("eMem") );
eMem.Free();
eMem.Trace( _T("eMem") );

Sortie :

### Object EMemory 'eMem' Trace #
# Object address='0x0012F468', status='EMemory::ObjectStatus_Enable' (code '3').
# Buffer address='0x00000004', workspace size='4' byte(s).
### End #

### Object EMemory 'eMem' Trace #
# Object address='0x0012F468', status='EMemory::ObjectStatus_Free' (code '1').
# Buffer address='0x00000000', workspace size='0' byte(s).
### End #

Vue :

00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E ..

00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E ..

Note : la fonction Free libère l'espace de travail mais pas l'objet lui-même. Tant que l'objet n'a pas été détruit automatiquement ou implicitement, il reste disponible pour allouer un autre espace de travail.

Si vous avez instancié un objet dynamiquement, vous devez le détruire explicitement sous peine de fuite mémoire :

Code :

EMemory *peMem = new EMemory( 4 );

..
.. code additionnel ..
..

delete peMem; // destruction de l'objet peMem instancié dynamiquement

 

 

 

 

Pointer les données

Lorsque l'objet EMemory alloue un espace de travail, il stocke l'adresse de celui-ci dans un pointeur membre protégé m_pBuffer.

Ce pointeur est de type void, c'est à dire indéterminé. Mais il est tout à fait possible de convertir (on dit aussi caster) ce type en un autre type pour pouvoir manipuler les données renfermées dans l'espace de travail (voir les fonctions de la famille Pointer...Get(...);).

Unicité virtuelle

Un pointeur permet de lire ou d'écrire des données ayant la même densité que son type.

C'est à dire d'un pointeur de type char ne pourra manipuler que des données de type char (1 octet), qu'un pointeur de type long ne pourra manipuler que des données de type long (4 octets), etc... Donc chaque octet de l'espace de travail ne doit pas être considéré comme une entité unitaire ad vitam eternam mais comme des éléments qui peuvent être regroupés au gré de leur utilisation. Il s'agit de virtualisation.

Pour un pointeur de type char, chaque octet de l'espace de travail est un entité propre car le type char permet de ne manipuler que des données ayant une densité de 1 octet. Par contre pour un pointeur de type long, ce sont des paquets de 4 octets qui sont des entités propres car le type long permet de ne manipuler que des données ayant une densité de 4 octets.

Le schéma suivant montre l'unicité des données d'un espace de travail si on le virtualise à partir d'un pointeur de type char :

00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E ..
00
<>
01
<>
02
<>
03
<>
04
<>
05
<>
06
<>
07
<>
41
A
42
B
43
C
44
D
45
E
46
F
47
G
48
H

Le schéma suivant montre l'unicité des données d'un espace de travail si on le virtualise à partir d'un pointeur de type long :

00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E ..
00
<=

=

=

=>
01
<=

=

=

=>
41
A
42
B
43
C
44
D
45
E
46
F
47
G
48
H

Le premier offset (offset 0x00) est situé à l'adresse 0x04, mais le second offset (0x01) n'est pas situé à l'adresse 0x05 mais à 0x08 car le type long a une densité de 4 octets. Il englobe 4 octets dans une même entité.

Lorsque l'on lit ou écrit des données à l'aide d'un pointeur long, ce n'est pas 1 octet qui est manipulé mais 4, c'est à dire le nombre d'octets correspondant à sa densité.

Description :

Instanciation d'un objet EMemory d'une taille de 8 octets d'espace de travail contenant les données 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47 et 0x48 ("ABCDEFGH" en ASCII).

Puis écriture dans chacun des octets de l'espace de travail de la valeur 0x5A ("Z" en ASCII) à l'aide d'un pointeur de type char (pc).

Enfin écriture dans chaque entité de 4 octets de l'espace de travail de la valeur 0x5A595857 ("ZYXW" en ASCII) à l'aide d'un pointeur de type long (pl).

Code :

EMemory eMem( "ABCDEFGH", 8 );
char *pc = eMem.PointerCharGet();
ulonglong ullCpt;

eMem.TraceEx( _T("eMem") );

for ( ullCpt = 0; ullCpt<eMem.SizeGet(); ullCpt++ )
  *pc++ = 'Z';

eMem.TraceEx( _T("eMem") );

long *pl = eMem.PointerLongGet();

// attention, il faut diviser la taille de l'espace
// de travail par 4 car un pointeur de type long a une
// densité de 4 octets. /!\ Sinon risque de plantage
// du programme /!\.
for ( ullCpt = 0; ullCpt<eMem.SizeGet()/4; ullCpt++ )
  *pl++ = 0x5A595857;

eMem.TraceEx( _T("eMem") );

Sortie :

### Object EMemory 'eMem' Trace #
# Object address='0x0012F468', status='EMemory::ObjectStatus_Enable' (code '3').
# Buffer address='0x00000004', workspace size='8' byte(s).
# Array data from offset '0x00000000' to offset '0x00000007', length=8 byte(s).
# Offset start 0x00000000 41 42 43 44 45 46 47 48 .. .. .. .. .. .. .. .. [ABCDEFGH]
### End #

### Object EMemory 'eMem' Trace #
# Object address='0x0012F468', status='EMemory::ObjectStatus_Enable' (code '3').
# Buffer address='0x00000004', workspace size='8' byte(s).
# Array data from offset '0x00000000' to offset '0x00000007', length=8 byte(s).
# Offset start 0x00000000 5A 5A 5A 5A 5A 5A 5A 5A .. .. .. .. .. .. .. .. [ZZZZZZZZ]
### End #

### Object EMemory 'eMem' Trace #
# Object address='0x0012F468', status='EMemory::ObjectStatus_Enable' (code '3').
# Buffer address='0x00000004', workspace size='8' byte(s).
# Array data from offset '0x00000000' to offset '0x00000007', length=8 byte(s).
# Offset start 0x00000000 57 58 59 5A 57 58 59 5A .. .. .. .. .. .. .. .. [WXYZWXYZ]
### End #

Vue :

00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E ..
00 01 02 03 04 05 06 07
41
A
42
B
43
C
44
D
45
E
46
F
47
G
48
H

00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E ..
00
<>
01
<>
02
<>
03
<>
04
<>
05
<>
06
<>
07
<>
5A
Z
5A
Z
5A
Z
5A
Z
5A
Z
5A
Z
5A
Z
5A
Z

00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E ..
00
<=

=

=

=>
01
<=

=

=

=>
57
W
58
X
59
Y
5A
Z
57
W
58
X
59
Y
5A
Z

Note : il ne faut pas être étonnée de trouver les valeurs 0x57, 0x58, 0x59, 0x5A, 0x57, 0x58, 0x59, 0x5A et non pas 0x5A, 0x59, 0x58, 0x57, 0x5A, 0x59, 0x58, 0x57 comme on pourrait s'y attendre. En effet, le processeur stocke les éléments d'un paquet de données en les inversant (technique dite little endian) mais les restitue dans le bon sens. Il ne faut pas appliquer notre logique humaine (big endian, de gauche à droite) à la logique du processeur (little endian, de droite à gauche).

Risque d'eccueuil

Si l'on utilise un pointeur qui a une densité supérieure à 1 octet, il faut être vigilant à ce qu'il y ait assez l'espace de travail lorsque lors de la manipulation de ce pointeur. En d'autres termes il faut que l'espace de travail soit un multiple de la densité du type de pointeur utilisé sinon il y a un gros risque de plantage.

Cela est d'autant plus dangereux que certaines fois le programme ne plante pas alors qu'il le devrait, donc vous pensez que votre code est valide. Mais tôt ou tard cela plantera, c'est inévitable, et il sera très difficile de retrouver l'origine de cette erreur impromptue.

Je vous le rappelle, l'écrasante majorité des plantages proviennent d'un accès illégal à une mémoire non autorisée.

Description :

Instanciation d'un objet EMemory d'une taille de 5 octets d'espace de travail contenant les données 0x41, 0x42, 0x43, 0x44, et 0x45 ("ABCDE" en ASCII).

Puis écriture, à deux reprises, dans chaque entité de 4 octets de l'espace de travail de la valeur 0x5A595857 ("ZYXW" en ASCII) à l'aide d'un pointeur de type long (pl).

La seconde tentative fait planter le programme car l'on tente d'écrire 4 octets à l'offset 1 mais l'espace de travail ne dispose plus que de 1 octet alors que le pointeur de type long tente d'écrire 4 octets. Il manque 3 octets pour ne pas faire planter le programme.

Code :

EMemory eMem( "ABCDE", 5 );
long *pl = eMem.PointerLongGet();

eMem.TraceEx( _T("eMem") );
*pl++ = 0x5A595857;
eMem.TraceEx( _T("eMem") );
*pl++ = 0x5A595857; // erreur, plantage, il manque 3 octets à l'espace de travail.

Sortie :

### Object EMemory 'eMem' Trace #
# Object address='0x0012F468', status='EMemory::ObjectStatus_Enable' (code '3').
# Buffer address='0x00000004', workspace size='5' byte(s).
# Array data from offset '0x00000000' to offset '0x00000004', length=6 byte(s).
# Offset start 0x00000000 41 42 43 44 45 .. .. .. .. .. .. .. .. .. .. .. [ABCDE]
### End #

### Object EMemory 'eMem' Trace #
# Object address='0x0012F468', status='EMemory::ObjectStatus_Enable' (code '3').
# Buffer address='0x00000004', workspace size='5' byte(s).
# Array data from offset '0x00000000' to offset '0x00000004', length=6 byte(s).
# Offset start 0x00000000 57 58 59 5A 45 .. .. .. .. .. .. .. .. .. .. .. [WXYZE]
### End #

Vue :

00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E ..
00 01 02 03 04
41
A
42
B
43
C
44
D
45
E

00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E ..
00
<=
 
=
 
=
 
=>
01
<=

=

=

=>
57
W
58
X
59
Y
5A
Z
45
E

00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E ..
00
<=
 
=
 
=
 
=>
01
<=
!!
=
!!
=
!!
=>
57
W
58
X
59
Y
5A
Z
57
W
? ? ?

 

 

 

 

 

Octet, série d'octets, bloc d'octets et série de blocs d'octets

Les fonctions de l'objet EMemory sont subdivisées en thématiques. Au sein de chacune de ces thématiques, ses fonctions peuvent travailler sur diverses organisations de données.

On peut, en gros, les subdiviser en quatre catégories :

L'octet

Chaque octet est considéré comme étant unitaire.

Description :

Recherche de l'octet 0x41 ("A" en ASCII) dans un objet EMemory d'une taille de 5 octets d'espace de travail contenant les données 0x41, 0x42, 0x43, 0x44 et 0x45 ("ABCDE" en ASCII).

Code :

EMemory eMem( "ABCDE", 5 );
ulonglong ullOffsetStart = 0;
ulonglong ullOffsetFound = 0;

eMem.TraceEx( _T("eMem") );

while ( eOk( eMem.ByteFind( &ullOffsetFound, 'A', ullOffsetStart ) ) && ullOffsetFound != EMemory::DataNotFound )
  {
  ETrace::DoEx( _T("Valeur 0x41 ('A') trouvée à l'offset %I64u.\n"), ullOffsetFound );
  ullOffsetStart = ullOffsetFound + 1;
  }

Sortie :

### Object EMemory 'eMem' Trace #
# Object address='0x0012F468', status='EMemory::ObjectStatus_Enable' (code '3').
# Buffer address='0x00000004', workspace size='5' byte(s).
# Array data from offset '0x00000000' to offset '0x00000004', length=5 byte(s).
# Offset start 0x00000000 41 42 43 44 45 .. .. .. .. .. .. .. .. .. .. .. [ABCDE]
### End #

Valeur 0x41 ('A') trouvée à l'offset 0.

Vue :

00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E ..
00 01 02 03 04
41
A
42
B
43
C
44
D
45
E

La série d'octets

Il s'agit d'une série d'octets dont chaque octet est considéré comme étant unitaire. Cela permet d'effectuer une série d'opérations lors d'un seul appel de fonction.

Description :

Recherche de l'octet 0x45 et/ou 0x43 et/ou 0x41 ("ECA" en ASCII) dans un objet EMemory d'une taille de 5 octets d'espace de travail contenant les données 0x41, 0x42, 0x43, 0x44 et 0x45 ("ABCDE" en ASCII).

Code :

EMemory eMem( "ABCDE", 5 );
ulonglong ullOffsetStart = 0;
ulonglong ullOffsetFound = 0;
uchar ucValueFound;

eMem.TraceEx( _T("eMem") );

while ( eOk( eMem.BytesFind( &ullOffsetFound, &ucValueFound, "ECA", 3, ullOffsetStart ) ) && ullOffsetFound != EMemory::DataNotFound )
  {
  ETrace::DoEx( _T("Valeur 0x%X ('%c') trouvée à l'offset %I64u.\n"), ucValueFound,   ucValueFound, ullOffsetFound );
  ullOffsetStart = ullOffsetFound + 1;
  }

Sortie :

### Object EMemory 'eMem' Trace #
# Object address='0x0012F468', status='EMemory::ObjectStatus_Enable' (code '3').
# Buffer address='0x00000004', workspace size='5' byte(s).
# Array data from offset '0x00000000' to offset '0x00000004', length=5 byte(s).
# Offset start 0x00000000 41 42 43 44 45 .. .. .. .. .. .. .. .. .. .. .. [ABCDE]
### End #

Valeur 0x41 ('A') trouvée à l'offset 0.
Valeur 0x43 ('C') trouvée à l'offset 2.
Valeur 0x45 ('E') trouvée à l'offset 4.

Vue :

00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E ..
00 01 02 03 04
41
A
42
B
43
C
44
D
45
E

L'exemple précédent ne cherche pas les données "ECA" mais soit la donnée "E" soit la donnée "C" soit la donnée "A". Lorsque la fonction BytesFind trouve l'une de ces valeurs, elle retourne le résultat de sa recherche.

Le bloc d'octets

Il s'agit d'une série d'octets dont l'ensemble des octets est considérée comme étant unitaire. Chaque octet n'est pas une entitié propre mais l'un des éléments d'un bloc.

Description :

Recherche du bloc d'octets 0x42, 0x43 et 0x44 ("BCD" en ASCII) dans un objet EMemory d'une taille de 5 octets d'espace de travail contenant les données 0x41, 0x42, 0x43, 0x44 et 0x45 ("ABCDE" en ASCII).

Code :

EMemory eMem( "ABCDE", 5 );
ulonglong ullOffsetStart = 0;
ulonglong ullOffsetFound = 0;

eMem.TraceEx( _T("eMem") );

while ( eOk( eMem.BlockFind( &ullOffsetFound, "BCD", 3, ullOffsetStart ) ) && ullOffsetFound != EMemory::DataNotFound )
  {
  ETrace::DoEx( _T("Bloc d'octets \"BCD\" trouvé à l'offset %I64u.\n"), ullOffsetFound );
  ullOffsetStart = ullOffsetFound + 3;
  }

Sortie :

### Object EMemory 'eMem' Trace #
# Object address='0x0012F468', status='EMemory::ObjectStatus_Enable' (code '3').
# Buffer address='0x00000004', workspace size='5' byte(s).
# Array data from offset '0x00000000' to offset '0x00000004', length=5 byte(s).
# Offset start 0x00000000 41 42 43 44 45 .. .. .. .. .. .. .. .. .. .. .. [ABCDE]
### End #

Bloc d'octets "BCD" trouvé à l'offset 1.

Vue :

00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E ..
00 01 02 03 04
41
A
42
B
43
C
44
D
45
E

L'exemple précédent ne cherche pas individuellement soit la donnée "B" soit la donnée "C" soit la donnée "D" mais trois octets contigus ayant respectivement les valeurs 0x42, 0x43 et 0x44 ("BCD" en ASCII).

La série de blocs d'octets

Il s'agit d'une série de blocs d'octets chaque bloc est considéré comme étant unitaire (pour plus d'informations voir la structure EMemory::SC_BLOCK). Cela permet d'effectuer une série d'opérations lors d'un seul appel de fonction.

Description :

Recherche soit d'un bloc d'octets contenant les données 0x41 et 0x42 ("AB" en ASCII) soit d'un bloc d'octets contenant les données 0x42 et 0x43 ("BC" en ASCII) soit d'un bloc d'octets contenant les données 0x43, 0x44 et 0x45 ("CDE" en ASCII) dans un objet EMemory d'une taille de 5 octets d'espace de travail contenant les données 0x41, 0x42, 0x43, 0x44 et 0x45 ("ABCDE" en ASCII).

Code :

EMemory eMem( "ABCDE", 5 );
ulonglong ullOffsetStart = 0;
ulonglong ullOffsetFound = 0;
ulonglong ullBlockIdxFound;
EMemory::SC_BLOCK sctBlock[]={
  { "AB", 2, 0, 0, 0 },
  { "BC", 2, 0, 0, 0 },
  { "CDE", 3, 0, 0, 0 }
  };

eMem.TraceEx( _T("eMem") );

while ( eOk( eMem.BlocksFind( &ullOffsetFound, &ullBlockIdxFound, sctBlock, 3, ullOffsetStart ) ) && ullOffsetFound != EMemory::DataNotFound )
  {
  ETrace::DoEx( _T("Bloc indexé trouvé = %I64u, à l'offset %I64u.\n"), ullBlockIdxFound, ullOffsetFound );
  ullOffsetStart = ullOffsetFound + sctBlock[ullBlockIdxFound].ullBlockSize;
  }

Sortie :

### Object EMemory 'eMem' Trace #
# Object address='0x0012F468', status='EMemory::ObjectStatus_Enable' (code '3').
# Buffer address='0x00000004', workspace size='5' byte(s).
# Array data from offset '0x00000000' to offset '0x00000004', length=5 byte(s).
# Offset start 0x00000000 41 42 43 44 45 .. .. .. .. .. .. .. .. .. .. .. [ABCDE]
### End #

Bloc indexé trouvé = 0, à l'offset 0.
Bloc indexé trouvé = 2, à l'offset 2.

Vue :

00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E ..
00 01 02 03 04
41
A
42
B
43
C
44
D
45
E