2025-11-24 22:52:51 +03:00

254 lines
8.7 KiB
Java

/*
* Decompiled with CFR 0.152.
*
* Could not load the following classes:
* com.mojang.logging.LogUtils
* org.jspecify.annotations.Nullable
* org.slf4j.Logger
*/
package net.minecraft.util.eventlog;
import com.mojang.logging.LogUtils;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.Reader;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;
import java.nio.channels.ReadableByteChannel;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.LinkOption;
import java.nio.file.OpenOption;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.nio.file.attribute.FileAttribute;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;
import org.jspecify.annotations.Nullable;
import org.slf4j.Logger;
public class EventLogDirectory {
private static final Logger LOGGER = LogUtils.getLogger();
private static final int COMPRESS_BUFFER_SIZE = 4096;
private static final String COMPRESSED_EXTENSION = ".gz";
private final Path root;
private final String extension;
private EventLogDirectory(Path root, String extension) {
this.root = root;
this.extension = extension;
}
public static EventLogDirectory open(Path root, String extension) throws IOException {
Files.createDirectories(root, new FileAttribute[0]);
return new EventLogDirectory(root, extension);
}
public FileList listFiles() throws IOException {
try (Stream<Path> list = Files.list(this.root);){
FileList fileList = new FileList(list.filter(x$0 -> Files.isRegularFile(x$0, new LinkOption[0])).map(this::parseFile).filter(Objects::nonNull).toList());
return fileList;
}
}
private @Nullable File parseFile(Path path) {
String fileName = path.getFileName().toString();
int extensionIndex = fileName.indexOf(46);
if (extensionIndex == -1) {
return null;
}
FileId id = FileId.parse(fileName.substring(0, extensionIndex));
if (id != null) {
String extension = fileName.substring(extensionIndex);
if (extension.equals(this.extension)) {
return new RawFile(path, id);
}
if (extension.equals(this.extension + COMPRESSED_EXTENSION)) {
return new CompressedFile(path, id);
}
}
return null;
}
private static void tryCompress(Path raw, Path compressed) throws IOException {
if (Files.exists(compressed, new LinkOption[0])) {
throw new IOException("Compressed target file already exists: " + String.valueOf(compressed));
}
try (FileChannel channel = FileChannel.open(raw, StandardOpenOption.WRITE, StandardOpenOption.READ);){
FileLock lock = channel.tryLock();
if (lock == null) {
throw new IOException("Raw log file is already locked, cannot compress: " + String.valueOf(raw));
}
EventLogDirectory.writeCompressed(channel, compressed);
channel.truncate(0L);
}
Files.delete(raw);
}
private static void writeCompressed(ReadableByteChannel channel, Path target) throws IOException {
try (GZIPOutputStream output = new GZIPOutputStream(Files.newOutputStream(target, new OpenOption[0]));){
byte[] bytes = new byte[4096];
ByteBuffer buffer = ByteBuffer.wrap(bytes);
while (channel.read(buffer) >= 0) {
buffer.flip();
((OutputStream)output).write(bytes, 0, buffer.limit());
buffer.clear();
}
}
}
public RawFile createNewFile(LocalDate date) throws IOException {
FileId id;
int index = 1;
Set<FileId> files = this.listFiles().ids();
while (files.contains(id = new FileId(date, index++))) {
}
RawFile file = new RawFile(this.root.resolve(id.toFileName(this.extension)), id);
Files.createFile(file.path(), new FileAttribute[0]);
return file;
}
public static class FileList
implements Iterable<File> {
private final List<File> files;
private FileList(List<File> files) {
this.files = new ArrayList<File>(files);
}
public FileList prune(LocalDate date, int expiryDays) {
this.files.removeIf(file -> {
FileId id = file.id();
LocalDate expiresAt = id.date().plusDays(expiryDays);
if (!date.isBefore(expiresAt)) {
try {
Files.delete(file.path());
return true;
}
catch (IOException e) {
LOGGER.warn("Failed to delete expired event log file: {}", (Object)file.path(), (Object)e);
}
}
return false;
});
return this;
}
public FileList compressAll() {
ListIterator<File> iterator = this.files.listIterator();
while (iterator.hasNext()) {
File file = iterator.next();
try {
iterator.set(file.compress());
}
catch (IOException e) {
LOGGER.warn("Failed to compress event log file: {}", (Object)file.path(), (Object)e);
}
}
return this;
}
@Override
public Iterator<File> iterator() {
return this.files.iterator();
}
public Stream<File> stream() {
return this.files.stream();
}
public Set<FileId> ids() {
return this.files.stream().map(File::id).collect(Collectors.toSet());
}
}
public record FileId(LocalDate date, int index) {
private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.BASIC_ISO_DATE;
public static @Nullable FileId parse(String name) {
int separator = name.indexOf("-");
if (separator == -1) {
return null;
}
String date = name.substring(0, separator);
String index = name.substring(separator + 1);
try {
return new FileId(LocalDate.parse(date, DATE_FORMATTER), Integer.parseInt(index));
}
catch (NumberFormatException | DateTimeParseException e) {
return null;
}
}
@Override
public String toString() {
return DATE_FORMATTER.format(this.date) + "-" + this.index;
}
public String toFileName(String extension) {
return String.valueOf(this) + extension;
}
}
public record RawFile(Path path, FileId id) implements File
{
public FileChannel openChannel() throws IOException {
return FileChannel.open(this.path, StandardOpenOption.WRITE, StandardOpenOption.READ);
}
@Override
public @Nullable Reader openReader() throws IOException {
return Files.exists(this.path, new LinkOption[0]) ? Files.newBufferedReader(this.path) : null;
}
@Override
public CompressedFile compress() throws IOException {
Path compressedPath = this.path.resolveSibling(this.path.getFileName().toString() + EventLogDirectory.COMPRESSED_EXTENSION);
EventLogDirectory.tryCompress(this.path, compressedPath);
return new CompressedFile(compressedPath, this.id);
}
}
public record CompressedFile(Path path, FileId id) implements File
{
@Override
public @Nullable Reader openReader() throws IOException {
if (!Files.exists(this.path, new LinkOption[0])) {
return null;
}
return new BufferedReader(new InputStreamReader((InputStream)new GZIPInputStream(Files.newInputStream(this.path, new OpenOption[0])), StandardCharsets.UTF_8));
}
@Override
public CompressedFile compress() {
return this;
}
}
public static interface File {
public Path path();
public FileId id();
public @Nullable Reader openReader() throws IOException;
public CompressedFile compress() throws IOException;
}
}