2017-07-21 1 views
1

Je crée un fichier contenant des objets de données. objet de données ont différentes tailles et sont quelque chose comme ça (très simplifié):Ecriture de données alignées dans un fichier binaire

struct Data{ 
    uint64_t size; 
    char  blob[MAX_SIZE]; 
// ... methods here: 
} 

À un stade ultérieur, le fichier sera mmap() en mémoire, donc je veux le début de tous les objets de données démarre sur adresse de mémoire alignée par 8 octets où uint64_t size seront stockés (ignorons l'endianness).

code

ressemble plus ou moins à ce (actuellement 8 octets) codées en dur:

size_t calcAlign(size_t const value, size_t const align_size){ 
    return align_size - value % align_size; 
} 

template<class ITERATOR> 
void process(std::ofstream &file_data, ITERATOR begin, ITERATOR end){ 
    for(auto it = begin; it != end; ++it){ 
     const auto &data = *it; 

     size_t bytesWriten = data.writeToFile(file_data); 

     size_t const alignToBeAdded = calcAlign(bytesWriten, 8); 

     if (alignToBeAdded != 8){ 
      uint64_t const placeholder = 0; 

      file_data.write((const char *) & placeholder, (std::streamsize) alignToBeAdded); 
     } 
    } 
} 

Est-ce la meilleure façon d'obtenir un alignement dans un fichier?

+0

Que voulez-vous dire par « l'alignement »? Pour écrire à une position spécifique dans le fichier? Avez-vous essayé de *** chercher *** dans le fichier au poste recherché? –

+0

Je vais modifier la question – Nick

+0

Donc, vous voulez que toutes les données soient au format 8 octets (64 bits). Est-ce exact? – Shirkam

Répondre

2

vous n'avez pas besoin de compter sur writeToFile pour retourner la taille, vous pouvez utiliser ofstream::tellp

const auto beginPos = file_data.tellp(); 
// write stuff to file 
const auto alignSize = (file_data.tellp()-beginPos)%8; 
if(alignSize) 
    file_data.write("\0\0\0\0\0\0\0\0",8-alignSize); 

EDIT commentaire post OP: testé sur un exemple minimal et il fonctionne.

#include <iostream> 
#include <fstream> 
int main(){ 
    using namespace std; 
    ofstream file_data; 
    file_data.open("tempfile.dat", ios::out | ios::binary); 
    const auto beginPos = file_data.tellp(); 
    file_data.write("something", 9); 
    const auto alignSize = (file_data.tellp() - beginPos) % 8; 
    if (alignSize) 
     file_data.write("\0\0\0\0\0\0\0\0", 8 - alignSize); 
    file_data.close(); 
    return 0; 
} 
+1

ofstream :: tellp - nice. J'utilise alignToBeAdded pour quelque chose d'autre, mais je le ferai avec ofstream :: tellp. à propos de temps - sont plus efficaces que calc et écrire avec une seule opération? (bon endroit pour utiliser ostream :: put (char) – Nick

+0

En fait, vous pouvez vous en débarrasser complètement et écrire une seule fois car c'est plus efficace et sûr car alignSize ne sera jamais> = 8. Code modifié – IlBeldus

+0

J'accepte la réponse , mais le code ne fonctionne pas – Nick

1

Vous pouvez optimiser le processus en manipulant le tampon d'entrée au lieu de la gestion des fichiers. Modifiez votre structure de données afin que le code qui remplit le tampon prenne en charge l'alignement.

struct Data{ 
    uint64_t size; 
    char blob[MAX_SIZE]; 
    // ... other methods here 

    // Ensure buffer alignment 
    static_assert(MAX_SIZE % 8 != 0, "blob size must be aligned to 8 bytes to avoid Buffer Overflow."); 

    uint64_t Fill(const char* data, uint64_t dataLength) { 
     // Validations... 

     memcpy(this->blob, data, dataLength); 
     this->size = dataLength; 

     const auto paddingLen = calcAlign(dataLength, 8) % 8; 
     if (padding > 0) { 
      memset(this->blob + dataLength, 0, paddingLen); 
     } 

     // Return the aligned size 
     return dataLength + paddingLen; 
    } 
}; 

Maintenant, quand vous passez les données à la fonction « process » utiliser simplement la taille de retour de remplissage, ce qui assure un alignement de 8 octets. De cette façon, vous vous occupez toujours de l'alignement manuellement, mais vous n'avez pas à écrire deux fois dans le fichier.

note: Ce code suppose que vous utilisez également Data comme tampon d'entrée. Vous devriez utiliser les mêmes principes si votre code utilise un autre objet pour contenir le tampon avant qu'il ne soit écrit dans le fichier.

Si vous pouvez utiliser POSIX, voir aussi pwrite

+0

J'ai fait quelque chose de similaire, je "pousse" à aligner les considérations dans la classe de données. – Nick