blob: 8c3d5ce1ff9025a6b0310d4ebdbfaa73dafd247a [file] [log] [blame]
/*
* Copyright 2017 HugeGraph Authors
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with this
* work for additional information regarding copyright ownership. The ASF
* licenses this file to You under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package com.baidu.hugegraph.computer.core.store.file.hgkvfile;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import com.baidu.hugegraph.computer.core.io.BufferedFileOutput;
import com.baidu.hugegraph.computer.core.util.BytesUtil;
import com.baidu.hugegraph.exception.NotSupportException;
import com.baidu.hugegraph.util.E;
public class HgkvDirImpl extends AbstractHgkvFile implements HgkvDir {
public static final String FILE_NAME_PREFIX = "hgkv_";
public static final String FILE_EXTEND_NAME = ".hgkv";
private static final String FILE_NAME_REGEX =
FILE_NAME_PREFIX + "[0-9]+" + FILE_EXTEND_NAME;
private static final Pattern FILE_NUM_PATTERN = Pattern.compile("[0-9]+");
private final List<HgkvFile> segments;
private HgkvDirImpl(String path) {
this(path, null);
}
private HgkvDirImpl(String path, List<HgkvFile> segments) {
super(path);
this.segments = segments;
}
public static HgkvDir create(String path) throws IOException {
File file = new File(path);
E.checkArgument(!file.exists(),
"Can't create HgkvDir because the " +
"directory already exists: '%s'", file.getPath());
file.mkdirs();
return new HgkvDirImpl(path);
}
public static HgkvDir open(String path) throws IOException {
E.checkArgumentNotNull(path, "Parameter path can't be null");
File file = new File(path);
E.checkArgument(file.exists(),
"Failed to open path because it does not exists: '%s'",
file.getPath());
E.checkArgument(file.isDirectory(),
"Failed to open path " +
"because it's not a directory: '%s'",
file.getPath());
return open(file);
}
private static File[] scanHgkvFiles(File dir) {
return dir.listFiles((dirName, name) -> name.matches(FILE_NAME_REGEX));
}
private static HgkvDir open(File file) throws IOException {
File[] files = scanHgkvFiles(file);
assert files != null && files.length != 0;
// Open segments
List<HgkvFile> segments = segmentsFromFiles(files);
// Set HgkvDir properties
HgkvDirImpl hgkvDir = new HgkvDirImpl(file.getPath(), segments);
hgkvDir.build();
return hgkvDir;
}
private static List<HgkvFile> segmentsFromFiles(File[] files)
throws IOException {
List<HgkvFile> segments = new ArrayList<>();
for (File file : files) {
segments.add(HgkvFileImpl.open(file));
}
segments.sort((o1, o2) -> {
int id1 = fileNameToSegmentId(o1.path());
int id2 = fileNameToSegmentId(o2.path());
return Integer.compare(id1, id2);
});
return segments;
}
private static int fileNameToSegmentId(String path) {
String fileName = Paths.get(path).getFileName().toString();
Matcher matcher = FILE_NUM_PATTERN.matcher(fileName);
E.checkState(matcher.find(),
"Can't get segment id from illegal file name: '%s'",
fileName);
return Integer.parseInt(matcher.group());
}
@Override
public void close() throws IOException {
// pass
}
@Override
public byte[] max() {
return this.max;
}
@Override
public byte[] min() {
return this.min;
}
@Override
public List<HgkvFile> segments() {
return this.segments;
}
@Override
public BufferedFileOutput output() throws FileNotFoundException {
throw new NotSupportException("Can't get output from HgkvDir");
}
private void build() throws IOException {
this.magic = MAGIC;
this.version = MAJOR_VERSION + "." + MINOR_VERSION;
this.numEntries = this.segments.stream()
.mapToLong(HgkvFile::numEntries)
.sum();
this.numSubEntries = this.segments.stream()
.mapToLong(HgkvFile::numSubEntries)
.sum();
this.max = this.segments.stream()
.map(HgkvFile::max)
.max(BytesUtil::compare)
.orElse(null);
this.min = this.segments.stream()
.map(HgkvFile::min)
.min(BytesUtil::compare)
.orElse(null);
// Close segments
for (HgkvFile segment : this.segments) {
segment.close();
}
}
}