package fr.iutfbleau.sae.util; import fr.iutfbleau.sae.util.BitOutputStream; import java.io.IOException; import java.io.OutputStream; /** * Décorateur de flux permettant l'écriture binaire à granularité du bit. *
* Cette classe encapsule un {@link OutputStream} existant et permet * l'écriture de bits individuellement ou par groupes. * Les bits sont accumulés afin de former des octets avant écriture. *
** Utilisée notamment pour l'encodage des fichiers compressés * (ex : format PIF utilisant des codes de Huffman). *
* @author Algassimou Pellel Diallo * @version 1.0 * @since 2025-12-13 */ public class BitOutputStream { /** Flux de sortie sous-jacent */ private final OutputStream fluxSortie; /** Octet en cours de construction */ private int octetEnConstruction; /** Position du prochain bit à écrire (de 7 à 0) */ private int positionBit; /** Indique si le flux est fermé */ private boolean fluxFerme; /** * Construit un écrivain binaire à partir d'un flux existant. * * @param fluxSortie flux de sortie à décorer * @throws IllegalArgumentException si le flux est nul */ public BitOutputStream(OutputStream fluxSortie) { if (fluxSortie == null) { throw new IllegalArgumentException("Le flux de sortie ne peut pas être nul"); } this.fluxSortie = fluxSortie; this.octetEnConstruction = 0; this.positionBit = 7; this.fluxFerme = false; } /** * Écrit un bit dans le flux binaire. * * @param bit bit à écrire (0 ou 1) * @throws IOException si une erreur d'écriture survient * @throws IllegalArgumentException si le bit n'est ni 0 ni 1 */ public void writeBit(int bit) throws IOException { if (bit != 0 && bit != 1) { throw new IllegalArgumentException("Le bit doit être 0 ou 1"); } if (fluxFerme) { throw new IOException("Le flux de sortie est fermé"); } if (bit == 1) { this.octetEnConstruction = this.octetEnConstruction | (1 << this.positionBit); } this.positionBit--; // si on atteint la fin de l'octet, on le grave dans le flux et rebolotte if(this.positionBit < 0){ this.fluxSortie.write(this.octetEnConstruction); this.octetEnConstruction = 0; this.positionBit = 7; } } /** * Écrit une séquence de bits correspondant à une valeur entière. * * @param valeur valeur contenant les bits à écrire * @param nombreBits nombre de bits à écrire (strictement positif) * @throws IOException si une erreur d'écriture survient */ public void writeBits(int valeur, int nombreBits) throws IOException { for (int i = nombreBits - 1; i >= 0; i--) { int bit = (valeur >> i) & 1; writeBit(bit); } } /** * Force l'écriture immédiate des données accumulées dans le flux sous-jacent. * * @throws IOException si une erreur survient lors du flush */ public void flush() throws IOException { if (fluxFerme) { throw new IOException("Le flux de sortie est fermé"); } while (this.positionBit >= 0) { writeBit(0); } this.fluxSortie.flush(); // Force l'écriture dans le flux sous-jacent dans le but de vider le buffer } /** * Vide les buffers internes et ferme le flux de sortie. * * @throws IOException si une erreur survient lors de la fermeture */ public void fermerFlux() throws IOException { // si le flux n'est pas déjà fermé if (!fluxFerme) { this.flush(); // compléter l'octet et forcer l'écriture this.fluxSortie.close(); // fermer le flux sous-jacent this.fluxFerme = true; // marquer le flux comme fermé } } }