Comment contourner l’absence de terminateur NUL dans les chaînes renvoyées par mmap ()?

Lorsque vous mappez () un fichier texte, comme ça

int fd = open("file.txt", O_RDWR); fstat(fd, &sb) char *text = mmap(0, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0); 

le contenu du fichier est mappé directement dans la mémoire, et le text qu’il contient ne contiendra pas de terminateur NUL, de sorte qu’il ne serait pas sûr de l’utiliser avec des fonctions de chaîne normales. Sur Linux (au moins), les octets restants de la page inutilisée sont remplis de zéro, si bien que vous obtenez un terminateur NUL dans tous les cas où la taille du fichier n’est pas un multiple de la taille de la page.

Mais compter sur ce qui semble sale et les autres implémentations de mmap() par exemple, dans FreeBSD, je pense) ne remplissent pas les pages partielles. Les fichiers de mappage qui sont des multiples de la taille de la page seront également dépourvus du terminateur NUL.

Existe-t-il des moyens raisonnables de contourner ce problème ou d’append le terminateur NUL?

Les choses que j’ai considérées

  1. Utiliser strn*() fonctionne exclusivement et suivre la distance jusqu’à la fin du tampon.
    • Avantages: pas besoin de terminateur NUL
    • Inconvénients: un suivi supplémentaire est nécessaire pour connaître la distance à la fin du fichier lors de l’parsing du texte; certaines fonctions str*() n’ont pas de strn*() , comme strstr .
  2. Comme une autre réponse a suggéré, faites un mappage anonyme à une adresse fixe après le mappage de votre fichier texte.
    • Avantages: Peut utiliser les fonctions régulières C str*()
    • Inconvénients: l’utilisation de MAP_FIXED n’est pas thread-safe; Semble un hack terrible de toute façon
  3. mmap() un octet supplémentaire et rendre la carte inscriptible, et écrire le terminateur NUL. La page de manuel mmap de OpenGroup indique que vous pouvez créer un mappage plus grand que la taille de votre object, mais que l’access aux données en dehors de l’object mappé génèrera un SIGBUS .
    • Avantages: Peut utiliser les fonctions régulières C str*()
    • Inconvénients: nécessite la manipulation (en ignorant?) SIGBUS , ce qui pourrait signifier que quelque chose d’autre s’est passé. Je ne suis pas vraiment sûr que l’écriture du terminateur NUL fonctionnera?
  4. Développez les fichiers avec des tailles qui sont des multiples de la taille de la page avec ftruncate() d’un octet.
    • Avantages: Peut utiliser les fonctions régulières C str*() ; ftruncate() va écrire un octet NUL dans la zone nouvellement allouée pour vous
    • Inconvénients: signifie que nous devons écrire dans les fichiers, ce qui peut ne pas être possible ou acceptable dans tous les cas; Ne résout pas le problème pour les implémentations mmap() qui ne remplissent pas les pages partielles
  5. Il suffit de read() le fichier dans une mémoire de malloc() et d’oublier mmap()
    • Avantages: évite toutes ces solutions; Facile à malloc() et octet supplémentaire pour NUL
    • Les inconvénients: Différentes caractéristiques de performance que mmap()

La solution n ° 1 semble généralement la meilleure et nécessite juste un travail supplémentaire de la part des fonctions de lecture du texte.

Existe-t-il de meilleures alternatives ou s’agit-il des meilleures solutions? Y a-t-il des aspects de ces solutions que je n’ai pas envisagés qui les rendent plus ou moins attrayants?

Je suggère de subir un changement de paradigme ici.

Vous regardez l’univers entier constitué de chaînes délimitées par des ‘\ 0’ qui définissent votre texte. Au lieu de regarder le monde de cette façon, pourquoi n’essayez-vous pas de regarder le monde où le texte est défini comme une séquence définie par un début et un iterator de fin.

Vous mmap votre fichier, puis définissez initialement l’iterator de début, appelez-le beg_iter au début du segment mmap-ed, et l’iterator de fin, appelez-le end_iter , au premier octet suivant le dernier octet du segment mmap-ed, ou beg_iter+number_of_pages*pagesize , puis jusqu’à

A) end_iter est égal à beg_iter , ou

B) beg_iter[-1] n’est pas un caractère nul, alors

C) décrémenter end_iter et revenir à l’étape A.

Lorsque vous avez terminé, vous avez une paire d’iterators, la valeur de l’iterator de début et la valeur de l’iterator de fin qui définit votre chaîne de texte.

Bien sûr, dans ce cas, vos iterators sont des caractères simples char * , mais ce n’est vraiment pas très important. Ce qui est important, c’est que vous vous retrouviez maintenant avec un riche ensemble d’algorithmes et de modèles de la bibliothèque standard C ++ à votre disposition, qui vous permettent d’implémenter de nombreuses opérations compliquées, mutables (comme std::transform ), et non mutables ( comme std::find ).

Les chaînes terminées par un point nul sont vraiment un gage de l’époque du simple C. Avec C ++, les chaînes terminées par un caractère nul sont quelque peu archaïques et banales. Le code C ++ moderne devrait utiliser std::ssortingng objects std::ssortingng et les séquences définies par les iterators de début et de fin.

Une petite note en bas de page: au lieu de déterminer combien de padding NULL vous avez fini avec mmap-ing (), vous pourriez trouver plus facile de fstat () le fichier et d’obtenir la longueur exacte du fichier, en octets, avant de le mapper. Ensuite, vous saurez maintenant exactement ce qui a été fait, et vous n’avez pas à l’inverser, en regardant le remplissage.