17 Commits

Author SHA1 Message Date
1a14d13303 build: Add SIMD optimizations to compare.c
Add SIMD (Single Instruction, Multiple Data) optimizations to the
compare.c file to improve performance. This includes vectorization
flags and architecture-specific optimizations to enhance the
processing of data in the comparison function. Additionally, the
implementation now dynamically selects an optimized code path based
on the size of the pattern data, ensuring efficient execution for
various scenarios.

By Lixfel
2024-04-27 20:07:46 +02:00
9c8551dc5a Move to i32 2023-12-27 20:49:21 +01:00
abb2084029 Move to i32 2023-12-27 20:44:37 +01:00
3eba89b119 Move to i32 2023-12-27 20:43:50 +01:00
511ce04f0b new Matcher 2023-12-26 21:28:46 +01:00
Chaoscaot
d20940f89b Improve Performance 2023-08-20 15:37:23 +02:00
Chaoscaot
e3e6e9f759 Improve Performance 2023-08-09 09:22:24 +02:00
Chaoscaot
ccae2ba393 Merge pull request #9 from Chaoscaot/dependabot/cargo/sqlx-0.7
Update sqlx requirement from 0.6 to 0.7
2023-07-11 20:48:13 +02:00
dependabot[bot]
6c6c95bedd Update sqlx requirement from 0.6 to 0.7
Updates the requirements on [sqlx](https://github.com/launchbadge/sqlx) to permit the latest version.
- [Changelog](https://github.com/launchbadge/sqlx/blob/main/CHANGELOG.md)
- [Commits](https://github.com/launchbadge/sqlx/compare/v0.6.0...v0.7.0)

---
updated-dependencies:
- dependency-name: sqlx
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-07-10 10:49:15 +00:00
Chaoscaot
582079c80d Bump Version 2023-05-23 20:10:41 +02:00
Chaoscaot
e25aeab065 Fix Broken Schematics Loading 2023-05-23 20:07:23 +02:00
Chaoscaot
aee3a80267 Reduce FLOPs 2023-05-01 11:32:40 +02:00
Chaoscaot
5107e04497 Update README.md 2023-04-28 00:28:34 +02:00
Chaoscaot
a357da2ce8 Fix Tests 2023-04-24 22:52:03 +02:00
Chaoscaot
eb84adb4a3 Reduce Boilerplate 2023-04-24 19:04:44 +02:00
Chaoscaot
25c4e97f71 Merge branch 'chaoscaot/support_sponge_v1_v3' 2023-04-23 12:19:22 +02:00
Chaoscaot
7d9e7f28a9 Fix SQL 2023-04-23 12:17:59 +02:00
18 changed files with 354 additions and 255 deletions

2
.gitignore vendored Normal file → Executable file
View File

@@ -3,3 +3,5 @@
/.idea/ /.idea/
/SchemSearch.class /SchemSearch.class
/SchemSearch.h /SchemSearch.h
/schemsearch-lib/src/.idea/
/schemsearch-lib/src/cmake-build-debug/

View File

@@ -16,4 +16,5 @@ opt-level = "z"
codegen-units = 1 codegen-units = 1
[profile.release] [profile.release]
lto = true lto = true
opt-level = 3

View File

@@ -1,10 +1,10 @@
default: default:
@echo "Building (Release)..."; @echo "Building (Release)...";
cargo rustc --release --color=always -p schemsearch-cli -- -C target-feature=+avx2 cargo rustc --release --color=always -p schemsearch-cli -- -C target-cpu=native
sql: sql:
@echo "Building (Release)..."; @echo "Building (Release)...";
cargo rustc --release --color=always -p schemsearch-cli --features sql -- -C target-feature=+avx2 cargo rustc --release --color=always -p schemsearch-cli --features sql -- -C target-cpu=native
debug: debug:
@echo "Building (Debug)..."; @echo "Building (Debug)...";

View File

@@ -1,5 +1,5 @@
# schemsearch # schemsearch
### A *simple* CLI tool to search in Sponge V2 Schematic files ### A *simple* CLI tool to search in Sponge Schematic files
--- ---

View File

@@ -1,6 +1,6 @@
[package] [package]
name = "schemsearch-cli" name = "schemsearch-cli"
version = "0.1.3" version = "0.1.6"
edition = "2021" edition = "2021"
license = "AGPL-3.0-or-later" license = "AGPL-3.0-or-later"
@@ -13,7 +13,7 @@ schemsearch-sql = { path = "../schemsearch-sql", optional = true }
clap = { version = "4.1.8", features = ["cargo"] } clap = { version = "4.1.8", features = ["cargo"] }
futures = { version = "0.3", optional = true } futures = { version = "0.3", optional = true }
sqlx = { version = "0.6", features = [ "runtime-async-std-native-tls" , "mysql" ], optional = true } sqlx = { version = "0.7", features = [ "runtime-async-std-native-tls" , "mysql" ], optional = true }
rayon = "1.7.0" rayon = "1.7.0"
indicatif = { version = "0.17.3", features = ["rayon"] } indicatif = { version = "0.17.3", features = ["rayon"] }
serde = "1.0.157" serde = "1.0.157"

View File

@@ -39,7 +39,7 @@ use schemsearch_sql::load_all_schematics;
#[cfg(feature = "sql")] #[cfg(feature = "sql")]
use crate::types::SqlSchematicSupplier; use crate::types::SqlSchematicSupplier;
use indicatif::*; use indicatif::*;
use schemsearch_files::{SchematicVersioned}; use schemsearch_files::SpongeSchematic;
use crate::sinks::{OutputFormat, OutputSink}; use crate::sinks::{OutputFormat, OutputSink};
use crate::stderr::MaschineStdErr; use crate::stderr::MaschineStdErr;
@@ -206,7 +206,7 @@ fn main() {
threshold: *matches.get_one::<f32>("threshold").expect("Couldn't get threshold"), threshold: *matches.get_one::<f32>("threshold").expect("Couldn't get threshold"),
}; };
let pattern = match SchematicVersioned::load(&PathBuf::from(matches.get_one::<String>("pattern").unwrap())) { let pattern = match SpongeSchematic::load(&PathBuf::from(matches.get_one::<String>("pattern").unwrap())) {
Ok(x) => x, Ok(x) => x,
Err(e) => { Err(e) => {
cmd.error(ErrorKind::Io, format!("Error while loading Pattern: {}", e.to_string())).exit(); cmd.error(ErrorKind::Io, format!("Error while loading Pattern: {}", e.to_string())).exit();
@@ -334,8 +334,8 @@ fn main() {
} }
} }
fn load_schem(schem_path: &PathBuf) -> Option<SchematicVersioned> { fn load_schem(schem_path: &PathBuf) -> Option<SpongeSchematic> {
match SchematicVersioned::load(schem_path) { match SpongeSchematic::load(schem_path) {
Ok(x) => Some(x), Ok(x) => Some(x),
Err(e) => { Err(e) => {
println!("Error while loading schematic ({}): {}", schem_path.to_str().unwrap(), e.to_string()); println!("Error while loading schematic ({}): {}", schem_path.to_str().unwrap(), e.to_string());

View File

@@ -15,11 +15,13 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
#[cfg(feature = "sql")]
use std::io::Cursor;
use std::path::PathBuf; use std::path::PathBuf;
#[cfg(feature = "sql")] #[cfg(feature = "sql")]
use futures::executor::block_on; use futures::executor::block_on;
#[allow(unused_imports)] #[cfg(feature = "sql")]
use schemsearch_files::SpongeV2Schematic; use schemsearch_files::SpongeSchematic;
#[cfg(feature = "sql")] #[cfg(feature = "sql")]
use schemsearch_sql::{load_schemdata, SchematicNode}; use schemsearch_sql::{load_schemdata, SchematicNode};
@@ -46,9 +48,9 @@ pub struct SqlSchematicSupplier {
#[cfg(feature = "sql")] #[cfg(feature = "sql")]
impl SqlSchematicSupplier { impl SqlSchematicSupplier {
pub fn get_schematic(&self) -> Result<SchematicVersioned, String> { pub fn get_schematic(&self) -> Result<SpongeSchematic, String> {
let schemdata = block_on(load_schemdata(self.node.id)); let mut schemdata = block_on(load_schemdata(self.node.id));
SchematicVersioned::load_data(schemdata.as_slice()) SpongeSchematic::load_data(&mut Cursor::new(schemdata.as_mut_slice()))
} }
pub fn get_name(&self) -> String { pub fn get_name(&self) -> String {

View File

@@ -1,6 +1,6 @@
[package] [package]
name = "schemsearch-files" name = "schemsearch-files"
version = "0.1.3" version = "0.1.5"
edition = "2021" edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

View File

@@ -21,92 +21,7 @@ use std::path::PathBuf;
use nbt::{CompoundTag, Tag}; use nbt::{CompoundTag, Tag};
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub enum SchematicVersioned { pub struct SpongeSchematic {
V1(SpongeV1Schematic),
V2(SpongeV2Schematic),
V3(SpongeV3Schematic),
}
impl SchematicVersioned {
#[inline]
pub fn get_width(&self) -> u16 {
return match self {
SchematicVersioned::V1(schematic) => schematic.width,
SchematicVersioned::V2(schematic) => schematic.width,
SchematicVersioned::V3(schematic) => schematic.width,
};
}
#[inline]
pub fn get_height(&self) -> u16 {
return match self {
SchematicVersioned::V1(schematic) => schematic.height,
SchematicVersioned::V2(schematic) => schematic.height,
SchematicVersioned::V3(schematic) => schematic.height,
};
}
#[inline]
pub fn get_length(&self) -> u16 {
return match self {
SchematicVersioned::V1(schematic) => schematic.length,
SchematicVersioned::V2(schematic) => schematic.length,
SchematicVersioned::V3(schematic) => schematic.length,
};
}
#[inline]
pub fn get_palette_max(&self) -> i32 {
return match self {
SchematicVersioned::V1(schematic) => schematic.palette_max,
SchematicVersioned::V2(schematic) => schematic.palette_max,
SchematicVersioned::V3(schematic) => schematic.blocks.palette.len() as i32,
};
}
#[inline]
pub fn get_palette(&self) -> &HashMap<String, i32> {
return match self {
SchematicVersioned::V1(schematic) => &schematic.palette,
SchematicVersioned::V2(schematic) => &schematic.palette,
SchematicVersioned::V3(schematic) => &schematic.blocks.palette,
};
}
#[inline]
pub fn get_block_data(&self) -> &Vec<i32> {
return match self {
SchematicVersioned::V1(schematic) => &schematic.block_data,
SchematicVersioned::V2(schematic) => &schematic.block_data,
SchematicVersioned::V3(schematic) => &schematic.blocks.block_data,
};
}
#[inline]
pub fn get_block_entities(&self) -> &Vec<BlockEntity> {
return match self {
SchematicVersioned::V1(schematic) => &schematic.tile_entities,
SchematicVersioned::V2(schematic) => &schematic.block_entities,
SchematicVersioned::V3(schematic) => &schematic.blocks.block_entities,
};
}
}
#[derive(Clone, Debug)]
pub struct SpongeV1Schematic {
pub metadata: CompoundTag,
pub width: u16,
pub height: u16,
pub length: u16,
pub offset: [i32; 3],
pub palette_max: i32,
pub palette: HashMap<String, i32>,
pub block_data: Vec<i32>,
pub tile_entities: Vec<BlockEntity>,
}
#[derive(Clone, Debug)]
pub struct SpongeV2Schematic {
pub data_version: i32, pub data_version: i32,
pub metadata: CompoundTag, pub metadata: CompoundTag,
pub width: u16, pub width: u16,
@@ -120,18 +35,6 @@ pub struct SpongeV2Schematic {
pub entities: Option<Vec<Entity>>, pub entities: Option<Vec<Entity>>,
} }
#[derive(Clone, Debug)]
pub struct SpongeV3Schematic {
pub data_version: i32,
pub metadata: CompoundTag,
pub width: u16,
pub height: u16,
pub length: u16,
pub offset: [i32; 3],
pub blocks: BlockContainer,
pub entities: Option<Vec<Entity>>,
}
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct BlockContainer { pub struct BlockContainer {
pub palette: HashMap<String, i32>, pub palette: HashMap<String, i32>,
@@ -158,28 +61,37 @@ pub struct Entity {
pub pos: [i32; 3], pub pos: [i32; 3],
} }
impl SchematicVersioned { impl SpongeSchematic {
pub fn load_data<R>(data: &mut R) -> Result<SchematicVersioned, String> where R: Read { pub fn load_data<R>(data: &mut R) -> Result<SpongeSchematic, String> where R: Read {
let nbt: CompoundTag = nbt::decode::read_gzip_compound_tag(data).map_err(|e| e.to_string())?; let nbt: CompoundTag = nbt::decode::read_gzip_compound_tag(data).map_err(|e| e.to_string())?;
let version = nbt.get_i32("Version").map_err(|e| e.to_string())?; let version = nbt.get_i32("Version").unwrap_or_else(|_| {
return if nbt.contains_key("Blocks") {
3
} else if nbt.contains_key("BlockEntities") {
2
} else if nbt.contains_key("TileEntities") {
1
} else {
-1
};
});
match version { match version {
1 => Ok(SchematicVersioned::V1(SpongeV1Schematic::from_nbt(nbt)?)), 1 => SpongeSchematic::from_nbt_1(nbt),
2 => Ok(SchematicVersioned::V2(SpongeV2Schematic::from_nbt(nbt)?)), 2 => SpongeSchematic::from_nbt_2(nbt),
3 => Ok(SchematicVersioned::V3(SpongeV3Schematic::from_nbt(nbt)?)), 3 => SpongeSchematic::from_nbt_3(nbt),
_ => Err("Invalid schematic: Unknown Version".to_string()), _ => Err("Invalid schematic: Unknown Version".to_string()),
} }
} }
pub fn load(path: &PathBuf) -> Result<SchematicVersioned, String> { pub fn load(path: &PathBuf) -> Result<SpongeSchematic, String> {
let mut file = std::fs::File::open(path).map_err(|e| e.to_string())?; let mut file = std::fs::File::open(path).map_err(|e| e.to_string())?;
Self::load_data(&mut file) Self::load_data(&mut file)
} }
}
impl SpongeV1Schematic { pub fn from_nbt_1(nbt: CompoundTag) -> Result<Self, String> {
pub fn from_nbt(nbt: CompoundTag) -> Result<Self, String> {
Ok(Self { Ok(Self {
data_version: 0,
metadata: nbt.get_compound_tag("Metadata").map_err(|e| e.to_string())?.clone(), metadata: nbt.get_compound_tag("Metadata").map_err(|e| e.to_string())?.clone(),
width: nbt.get_i16("Width").map_err(|e| e.to_string())? as u16, width: nbt.get_i16("Width").map_err(|e| e.to_string())? as u16,
height: nbt.get_i16("Height").map_err(|e| e.to_string())? as u16, height: nbt.get_i16("Height").map_err(|e| e.to_string())? as u16,
@@ -188,13 +100,12 @@ impl SpongeV1Schematic {
palette_max: nbt.get_i32("PaletteMax").map_err(|e| e.to_string())?, palette_max: nbt.get_i32("PaletteMax").map_err(|e| e.to_string())?,
palette: read_palette(nbt.get_compound_tag("Palette").map_err(|e| e.to_string())?), palette: read_palette(nbt.get_compound_tag("Palette").map_err(|e| e.to_string())?),
block_data: read_blocks(nbt.get_i8_vec("BlockData").map_err(|e| e.to_string())?), block_data: read_blocks(nbt.get_i8_vec("BlockData").map_err(|e| e.to_string())?),
tile_entities: read_tile_entities(nbt.get_compound_tag_vec("TileEntities").map_err(|e| e.to_string())?)?, block_entities: read_tile_entities(nbt.get_compound_tag_vec("TileEntities").unwrap_or_else(|_| vec![]))?,
entities: None,
}) })
} }
}
impl SpongeV2Schematic { pub fn from_nbt_2(nbt: CompoundTag) -> Result<Self, String> {
pub fn from_nbt(nbt: CompoundTag) -> Result<Self, String> {
Ok(Self{ Ok(Self{
data_version: nbt.get_i32("DataVersion").map_err(|e| e.to_string())?, data_version: nbt.get_i32("DataVersion").map_err(|e| e.to_string())?,
metadata: nbt.get_compound_tag("Metadata").map_err(|e| e.to_string())?.clone(), metadata: nbt.get_compound_tag("Metadata").map_err(|e| e.to_string())?.clone(),
@@ -205,14 +116,12 @@ impl SpongeV2Schematic {
palette_max: nbt.get_i32("PaletteMax").map_err(|e| e.to_string())?, palette_max: nbt.get_i32("PaletteMax").map_err(|e| e.to_string())?,
palette: read_palette(nbt.get_compound_tag("Palette").map_err(|e| e.to_string())?), palette: read_palette(nbt.get_compound_tag("Palette").map_err(|e| e.to_string())?),
block_data: read_blocks(nbt.get_i8_vec("BlockData").map_err(|e| e.to_string())?), block_data: read_blocks(nbt.get_i8_vec("BlockData").map_err(|e| e.to_string())?),
block_entities: read_tile_entities(nbt.get_compound_tag_vec("BlockEntities").map_err(|e| e.to_string())?)?, block_entities: read_tile_entities(nbt.get_compound_tag_vec("BlockEntities").unwrap_or_else(|_| vec![]))?,
entities: None, entities: None,
}) })
} }
}
impl SpongeV3Schematic { pub fn from_nbt_3(nbt: CompoundTag) -> Result<Self, String> {
pub fn from_nbt(nbt: CompoundTag) -> Result<Self, String> {
let blocks = nbt.get_compound_tag("Blocks").map_err(|e| e.to_string())?; let blocks = nbt.get_compound_tag("Blocks").map_err(|e| e.to_string())?;
Ok(Self{ Ok(Self{
data_version: nbt.get_i32("DataVersion").map_err(|e| e.to_string())?, data_version: nbt.get_i32("DataVersion").map_err(|e| e.to_string())?,
@@ -221,14 +130,14 @@ impl SpongeV3Schematic {
height: nbt.get_i16("Height").map_err(|e| e.to_string())? as u16, height: nbt.get_i16("Height").map_err(|e| e.to_string())? as u16,
length: nbt.get_i16("Length").map_err(|e| e.to_string())? as u16, length: nbt.get_i16("Length").map_err(|e| e.to_string())? as u16,
offset: read_offset(nbt.get_i32_vec("Offset").map_err(|e| e.to_string())?)?, offset: read_offset(nbt.get_i32_vec("Offset").map_err(|e| e.to_string())?)?,
blocks: BlockContainer { palette_max: compute_palette_max(blocks.get_compound_tag("Palette").map_err(|e| e.to_string())?),
palette: read_palette(blocks.get_compound_tag("Palette").map_err(|e| e.to_string())?), palette: read_palette(blocks.get_compound_tag("Palette").map_err(|e| e.to_string())?),
block_data: read_blocks(blocks.get_i8_vec("BlockData").map_err(|e| e.to_string())?), block_data: read_blocks(blocks.get_i8_vec("BlockData").map_err(|e| e.to_string())?),
block_entities: read_tile_entities(blocks.get_compound_tag_vec("BlockEntities").map_err(|e| e.to_string())?)?, block_entities: read_tile_entities(blocks.get_compound_tag_vec("BlockEntities").unwrap_or_else(|_| vec![]))?,
},
entities: None, entities: None,
}) })
} }
} }
fn read_tile_entities(tag: Vec<&CompoundTag>) -> Result<Vec<BlockEntity>, String> { fn read_tile_entities(tag: Vec<&CompoundTag>) -> Result<Vec<BlockEntity>, String> {
@@ -262,6 +171,14 @@ fn read_palette(p: &CompoundTag) -> HashMap<String, i32> {
palette palette
} }
#[inline]
fn compute_palette_max(palette: &CompoundTag) -> i32 {
palette.iter().map(|(_, v)| v).filter_map(|v| match v {
Tag::Int(n) => Some(*n),
_ => None,
}).max().unwrap_or(0)
}
#[inline] #[inline]
fn read_blocks(blockdata: &Vec<i8>) -> Vec<i32> { fn read_blocks(blockdata: &Vec<i8>) -> Vec<i32> {
read_varint_array(blockdata) read_varint_array(blockdata)

View File

@@ -1,6 +1,6 @@
[package] [package]
name = "schemsearch-lib" name = "schemsearch-lib"
version = "0.1.3" version = "0.1.6"
edition = "2021" edition = "2021"
license = "AGPL-3.0-or-later" license = "AGPL-3.0-or-later"
@@ -10,3 +10,7 @@ license = "AGPL-3.0-or-later"
serde = { version = "1.0.160", features = ["derive"] } serde = { version = "1.0.160", features = ["derive"] }
schemsearch-files = { path = "../schemsearch-files" } schemsearch-files = { path = "../schemsearch-files" }
named-binary-tag = "0.6" named-binary-tag = "0.6"
libmath = "0.2.1"
[build-dependencies]
cc = { version = "1.0.83", features = [] }

11
schemsearch-lib/build.rs Executable file
View File

@@ -0,0 +1,11 @@
use cc;
fn main() {
cc::Build::new()
.file("src/compare.c")
.flag("-ftree-vectorize")
.flag("-march=native")
.flag("-mtune=native")
.flag("-ffast-math")
.compile("compare");
}

View File

@@ -0,0 +1,7 @@
cmake_minimum_required(VERSION 3.27)
project(src C)
set(CMAKE_C_STANDARD 11)
add_executable(src
compare.c)

110
schemsearch-lib/src/compare.c Executable file
View File

@@ -0,0 +1,110 @@
#include <stdint.h>
#include <stddef.h>
#include <stdlib.h>
int32_t isMatching(
const int32_t *schem_data,
const int32_t *pattern_data,
size_t pattern_data_length,
int32_t x,
int32_t y,
int32_t z,
int32_t schem_width,
int32_t schem_length,
int32_t pattern_width,
int32_t pattern_height,
int32_t pattern_length,
int32_t *w_ptr
) {
for (int j = 0; j < pattern_height; ++j) {
for (int k = 0; k < pattern_length; ++k) {
int pattern_index_pre = k * pattern_width + j * pattern_width * pattern_length;
int schem_index_pre = x + (k + z) * schem_width + (j + y) * schem_width * schem_length;
for (int i = 0; i < pattern_width; ++i) {
int pattern_index = i + pattern_index_pre;
int schem_index = i + schem_index_pre;
w_ptr[pattern_index] = schem_data[schem_index];
}
}
}
int32_t matching = 0;
for (int i = 0; i < pattern_data_length; ++i) {
matching += w_ptr[i] == pattern_data[i];
}
return matching;
}
void is_matching_all(
const int32_t *__restrict__ schem_data,
const int32_t *__restrict__ pattern_data,
int32_t schem_width,
int32_t schem_height,
int32_t schem_length,
int32_t pattern_width,
int32_t pattern_height,
int32_t pattern_length,
int32_t *__restrict__ result
) {
if(pattern_width*pattern_height*pattern_length >= 65536) { //TODO check for table size < 65536
for (int32_t pz = 0; pz < pattern_length; ++pz) {
int32_t maxZ = schem_length - pattern_length + pz + 1;
for (int32_t py = 0; py < pattern_height; ++py) {
int32_t maxY = schem_height - pattern_height + py + 1;
for (int32_t px = 0; px < pattern_width; ++px) {
int32_t pv = pattern_data[px + py * pattern_width + pz * pattern_width * pattern_height];
int32_t maxX = schem_width - pattern_width + px + 1;
for (int32_t z = pz; z < maxZ; ++z) {
int32_t sourceOffsetZ = z * schem_width * schem_height;
int32_t resultOffsetZ = (z - pz) * schem_width * schem_height - py * schem_width;
for (int32_t y = py; y < maxY; ++y) {
int32_t sourceOffsetY = sourceOffsetZ + y * schem_width;
int32_t resultOffsetY = resultOffsetZ + y * schem_width - px;
for (int32_t x = px; x < maxX; ++x) {
result[resultOffsetY + x] += schem_data[sourceOffsetY + x] == pv;
}
}
}
}
}
}
} else {
size_t schem_size = schem_width*schem_height*schem_length;
uint16_t *__restrict__ sschem_data = (uint16_t*)malloc(schem_size*2);
uint16_t *__restrict__ sresult = (uint16_t*)malloc(schem_size*2);
for(size_t i = 0; i < schem_size; i++) {
sschem_data[i] = schem_data[i];
sresult[i] = 0;
}
for (int32_t pz = 0; pz < pattern_length; ++pz) {
int32_t maxZ = schem_length - pattern_length + pz + 1;
for (int32_t py = 0; py < pattern_height; ++py) {
int32_t maxY = schem_height - pattern_height + py + 1;
for (int32_t px = 0; px < pattern_width; ++px) {
uint16_t pv = (uint16_t)pattern_data[px + py * pattern_width + pz * pattern_width * pattern_height];
int32_t maxX = schem_width - pattern_width + px + 1;
for (int32_t z = pz; z < maxZ; ++z) {
int32_t sourceOffsetZ = z * schem_width * schem_height;
int32_t resultOffsetZ = (z - pz) * schem_width * schem_height - py * schem_width;
for (int32_t y = py; y < maxY; ++y) {
int32_t sourceOffsetY = sourceOffsetZ + y * schem_width;
int32_t resultOffsetY = resultOffsetZ + y * schem_width - px;
for (int32_t x = px; x < maxX; ++x) {
sresult[resultOffsetY + x] += sschem_data[sourceOffsetY + x] == pv;
}
}
}
}
}
}
for(size_t i = 0; i < schem_size; i++) {
result[i] = sresult[i];
}
free(sschem_data);
free(sresult);
}
}

View File

@@ -19,8 +19,9 @@ pub mod pattern_mapper;
use serde::{Serialize, Deserialize}; use serde::{Serialize, Deserialize};
use pattern_mapper::match_palette; use pattern_mapper::match_palette;
use schemsearch_files::SchematicVersioned; use schemsearch_files::SpongeSchematic;
use crate::pattern_mapper::match_palette_adapt; use crate::pattern_mapper::match_palette_adapt;
use math::round::ceil;
#[derive(Debug, Clone, Copy, Deserialize, Serialize)] #[derive(Debug, Clone, Copy, Deserialize, Serialize)]
pub struct SearchBehavior { pub struct SearchBehavior {
@@ -32,79 +33,150 @@ pub struct SearchBehavior {
pub threshold: f32, pub threshold: f32,
} }
extern "C" {
pub fn isMatching(
schem_data: *const i32,
pattern_data: *const i32,
pattern_data_length: usize,
x: usize,
y: usize,
z: usize,
schem_width: usize,
schem_length: usize,
pattern_width: usize,
pattern_height: usize,
pattern_length: usize,
w_ptr: *mut i32,
) -> i32;
pub fn is_matching_all(
schem_data: *const i32,
pattern_data: *const i32,
schem_width: i32,
schem_height: i32,
schem_length: i32,
pattern_width: i32,
pattern_height: i32,
pattern_length: i32,
result: *mut i32
);
}
pub fn search( pub fn search(
schem: SchematicVersioned, schem: SpongeSchematic,
pattern_schem: &SchematicVersioned, pattern_schem: &SpongeSchematic,
search_behavior: SearchBehavior, search_behavior: SearchBehavior,
) -> Vec<Match> { ) -> Vec<Match> {
if schem.get_width() < pattern_schem.get_width() || schem.get_height() < pattern_schem.get_height() || schem.get_length() < pattern_schem.get_length() { if schem.width < pattern_schem.width || schem.height < pattern_schem.height || schem.length < pattern_schem.length {
return vec![]; return Vec::new();
} }
if pattern_schem.get_palette().len() > schem.get_palette().len() { if pattern_schem.palette.len() > schem.palette.len() {
return vec![]; return Vec::new();
} }
let pattern_schem = match_palette(&schem, &pattern_schem, search_behavior.ignore_block_data); let pattern_schem = match_palette(&schem, &pattern_schem, search_behavior.ignore_block_data);
let mut matches: Vec<Match> = Vec::new(); let mut matches: Vec<Match> = Vec::with_capacity(4);
let pattern_data = pattern_schem.get_block_data().as_slice(); let pattern_data = pattern_schem.block_data.as_ptr();
let schem_data = if search_behavior.ignore_block_data { let schem_data = if search_behavior.ignore_block_data {
match_palette_adapt(&schem, &pattern_schem.get_palette(), search_behavior.ignore_block_data) match_palette_adapt(&schem, &pattern_schem.palette, search_behavior.ignore_block_data)
} else { } else {
schem.get_block_data().clone() schem.block_data
}; };
let schem_data = schem_data.as_slice(); let schem_data = schem_data.as_ptr();
let air_id = if search_behavior.ignore_air || search_behavior.air_as_any { pattern_schem.get_palette().get("minecraft:air").unwrap_or(&-1) } else { &-1}; let air_id = if search_behavior.ignore_air || search_behavior.air_as_any { pattern_schem.palette.get("minecraft:air").unwrap_or(&-1) } else { &-1};
let pattern_blocks = pattern_data.len() as f32; let pattern_blocks_usize = pattern_schem.block_data.len();
let pattern_blocks = pattern_blocks_usize as f32;
let i_pattern_blocks = pattern_blocks as i32;
let pattern_width = pattern_schem.get_width() as usize; let pattern_width = pattern_schem.width as usize;
let pattern_height = pattern_schem.get_height() as usize; let pattern_height = pattern_schem.height as usize;
let pattern_length = pattern_schem.get_length() as usize; let pattern_length = pattern_schem.length as usize;
let schem_width = schem.get_width() as usize; let schem_width = schem.width as usize;
let schem_height = schem.get_height() as usize; let schem_height = schem.height as usize;
let schem_length = schem.get_length() as usize; let schem_length = schem.length as usize;
for y in 0..=schem_height - pattern_height { let skip_amount = ceil((pattern_blocks * (1.0 - search_behavior.threshold)) as f64, 0) as i32;
/*for y in 0..=schem_height - pattern_height {
for z in 0..=schem_length - pattern_length { for z in 0..=schem_length - pattern_length {
for x in 0..=schem_width - pattern_width { for x in 0..=schem_width - pattern_width {
let mut matching = 0; let matching_count;
for j in 0..pattern_height { unsafe {
for k in 0..pattern_length { matching_count = isMatching(
for i in 0..pattern_width { schem_data,
let index = (x + i) + schem_width * ((z + k) + (y + j) * schem_length); pattern_data,
let pattern_index = i + pattern_width * (k + j * pattern_length); pattern_blocks_usize,
let data = unsafe {schem_data.get_unchecked(index) }; x,
let pattern_data = unsafe { pattern_data.get_unchecked(pattern_index) }; y,
if *data == *pattern_data || (search_behavior.ignore_air && *data == *air_id) || (search_behavior.air_as_any && *pattern_data == *air_id) { z,
matching += 1; schem_width,
} schem_length,
} pattern_width,
pattern_height,
pattern_length,
w_ptr,
);
};
if matching_count >= i_pattern_blocks - skip_amount {
let percent = matching_count as f32 / pattern_blocks;
if percent >= search_behavior.threshold {
matches.push(Match {
x: x as u16,
y: y as u16,
z: z as u16,
percent,
});
} }
} }
let matching_percent = matching as f32 / pattern_blocks;
if matching_percent >= search_behavior.threshold {
matches.push(Match {
x: x as u16,
y: y as u16,
z: z as u16,
percent: matching_percent,
});
}
} }
} }
}*/
let mut result = Vec::<i32>::with_capacity(schem_width * schem_height * schem_length);
result.resize(schem_width * schem_height * schem_length, 0);
unsafe {
is_matching_all(
schem_data,
pattern_data,
schem_width as i32,
schem_height as i32,
schem_length as i32,
pattern_width as i32,
pattern_height as i32,
pattern_length as i32,
result.as_mut_ptr()
);
} }
result.into_iter().enumerate().filter(|(_, matching_count)| *matching_count >= i_pattern_blocks - skip_amount).for_each(|(i, matching_count)| {
let percent = matching_count as f32 / pattern_blocks;
let x = i % schem_width;
let y = (i / schem_width) % schem_height;
let z = i / (schem_width * schem_height);
matches.push(Match {
x: x as u16,
y: y as u16,
z: z as u16,
percent,
});
});
return matches; return matches;
} }
#[derive(Debug, Clone, Copy, Deserialize, Serialize)] #[derive(Debug, Clone, Copy, Default, Deserialize, Serialize)]
pub struct Match { pub struct Match {
pub x: u16, pub x: u16,
pub y: u16, pub y: u16,
@@ -112,17 +184,6 @@ pub struct Match {
pub percent: f32, pub percent: f32,
} }
impl Default for Match {
fn default() -> Self {
Self {
x: 0,
y: 0,
z: 0,
percent: 0.0,
}
}
}
#[inline] #[inline]
pub fn normalize_data(data: &str, ignore_data: bool) -> &str { pub fn normalize_data(data: &str, ignore_data: bool) -> &str {
if ignore_data { if ignore_data {
@@ -136,19 +197,12 @@ pub fn normalize_data(data: &str, ignore_data: bool) -> &str {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use schemsearch_files::SchematicVersioned::V2;
use schemsearch_files::SpongeV2Schematic;
use crate::pattern_mapper::strip_data; use crate::pattern_mapper::strip_data;
use super::*; use super::*;
#[test] #[test]
fn read_schematic() { fn read_schematic() {
let schematic = SchematicVersioned::load(&PathBuf::from("../tests/simple.schem")).unwrap(); let schematic = SpongeSchematic::load(&PathBuf::from("../tests/simple.schem")).unwrap();
let schematic = match schematic {
V2 (schematic) => schematic,
_ => panic!("Invalid schematic version"),
};
assert_eq!(schematic.width as usize * schematic.height as usize * schematic.length as usize, schematic.block_data.len()); assert_eq!(schematic.width as usize * schematic.height as usize * schematic.length as usize, schematic.block_data.len());
assert_eq!(schematic.palette_max, schematic.palette.len() as i32); assert_eq!(schematic.palette_max, schematic.palette.len() as i32);
@@ -156,12 +210,7 @@ mod tests {
#[test] #[test]
fn test_parse_function() { fn test_parse_function() {
let schematic: SchematicVersioned = SchematicVersioned::load(&PathBuf::from("../tests/simple.schem")).unwrap(); let schematic = SpongeSchematic::load(&PathBuf::from("../tests/simple.schem")).unwrap();
let schematic = match schematic {
V2 (schematic) => schematic,
_ => panic!("Invalid schematic version"),
};
assert_eq!(schematic.width as usize * schematic.height as usize * schematic.length as usize, schematic.block_data.len()); assert_eq!(schematic.width as usize * schematic.height as usize * schematic.length as usize, schematic.block_data.len());
assert_eq!(schematic.palette_max, schematic.palette.len() as i32); assert_eq!(schematic.palette_max, schematic.palette.len() as i32);
@@ -169,32 +218,32 @@ mod tests {
#[test] #[test]
fn test_strip_schem() { fn test_strip_schem() {
let schematic = SchematicVersioned::load(&PathBuf::from("../tests/simple.schem")).unwrap(); let schematic = SpongeSchematic::load(&PathBuf::from("../tests/simple.schem")).unwrap();
let stripped = strip_data(&schematic); let stripped = strip_data(&schematic);
assert_eq!(stripped.get_palette().keys().any(|k| k.contains('[')), false); assert_eq!(stripped.palette.keys().any(|k| k.contains('[')), false);
} }
#[test] #[test]
fn test_match_palette() { fn test_match_palette() {
let schematic = SchematicVersioned::load(&PathBuf::from("../tests/simple.schem")).unwrap(); let schematic = SpongeSchematic::load(&PathBuf::from("../tests/simple.schem")).unwrap();
let endstone = SchematicVersioned::load(&PathBuf::from("../tests/endstone.schem")).unwrap(); let endstone = SpongeSchematic::load(&PathBuf::from("../tests/endstone.schem")).unwrap();
let _ = match_palette(&schematic, &endstone, true); let _ = match_palette(&schematic, &endstone, true);
} }
#[test] #[test]
fn test_match_palette_ignore_data() { fn test_match_palette_ignore_data() {
let schematic = SchematicVersioned::load(&PathBuf::from("../tests/simple.schem")).unwrap(); let schematic = SpongeSchematic::load(&PathBuf::from("../tests/simple.schem")).unwrap();
let endstone = SchematicVersioned::load(&PathBuf::from("../tests/endstone.schem")).unwrap(); let endstone = SpongeSchematic::load(&PathBuf::from("../tests/endstone.schem")).unwrap();
let _ = match_palette(&schematic, &endstone, false); let _ = match_palette(&schematic, &endstone, false);
} }
#[test] #[test]
pub fn test_big_search() { pub fn test_big_search() {
let schematic = SchematicVersioned::load(&PathBuf::from("../tests/simple.schem")).unwrap(); let schematic = SpongeSchematic::load(&PathBuf::from("../tests/simple.schem")).unwrap();
let endstone = SchematicVersioned::load(&PathBuf::from("../tests/endstone.schem")).unwrap(); let endstone = SpongeSchematic::load(&PathBuf::from("../tests/endstone.schem")).unwrap();
let _ = search(schematic, &endstone, SearchBehavior { let _ = search(schematic, &endstone, SearchBehavior {
ignore_block_data: true, ignore_block_data: true,
@@ -208,8 +257,8 @@ mod tests {
#[test] #[test]
pub fn test_search() { pub fn test_search() {
let schematic = SchematicVersioned::load(&PathBuf::from("../tests/Random.schem")).unwrap(); let schematic = SpongeSchematic::load(&PathBuf::from("../tests/Random.schem")).unwrap();
let pattern = SchematicVersioned::load(&PathBuf::from("../tests/Pattern.schem")).unwrap(); let pattern = SpongeSchematic::load(&PathBuf::from("../tests/Pattern.schem")).unwrap();
let matches = search(schematic, &pattern, SearchBehavior { let matches = search(schematic, &pattern, SearchBehavior {
ignore_block_data: true, ignore_block_data: true,
@@ -220,7 +269,6 @@ mod tests {
threshold: 0.9 threshold: 0.9
}); });
println!("{:?}", matches);
assert_eq!(matches.len(), 1); assert_eq!(matches.len(), 1);
assert_eq!(matches[0].x, 1); assert_eq!(matches[0].x, 1);
assert_eq!(matches[0].y, 0); assert_eq!(matches[0].y, 0);
@@ -230,8 +278,8 @@ mod tests {
#[test] #[test]
pub fn test_search_ws() { pub fn test_search_ws() {
let schematic = SchematicVersioned::load(&PathBuf::from("../tests/warships/GreyFly-by-Bosslar.schem")).unwrap(); let schematic = SpongeSchematic::load(&PathBuf::from("../tests/warships/GreyFly-by-Bosslar.schem")).unwrap();
let pattern = SchematicVersioned::load(&PathBuf::from("../tests/gray_castle_complex.schem")).unwrap(); let pattern = SpongeSchematic::load(&PathBuf::from("../tests/gray_castle_complex.schem")).unwrap();
let matches = search(schematic, &pattern, SearchBehavior { let matches = search(schematic, &pattern, SearchBehavior {
ignore_block_data: false, ignore_block_data: false,
@@ -242,7 +290,6 @@ mod tests {
threshold: 0.9 threshold: 0.9
}); });
println!("{:?}", matches);
assert_eq!(matches.len(), 1); assert_eq!(matches.len(), 1);
} }
} }

View File

@@ -17,27 +17,27 @@
use std::collections::HashMap; use std::collections::HashMap;
use nbt::CompoundTag; use nbt::CompoundTag;
use schemsearch_files::{SchematicVersioned, SpongeV2Schematic}; use schemsearch_files::SpongeSchematic;
use crate::normalize_data; use crate::normalize_data;
fn create_reverse_palette(schem: &SchematicVersioned) -> Vec<&str> { fn create_reverse_palette(schem: &SpongeSchematic) -> Vec<&str> {
let mut reverse_palette = Vec::with_capacity(schem.get_palette_max() as usize); let mut reverse_palette = Vec::with_capacity(schem.palette_max as usize);
(0..schem.get_palette_max()).for_each(|_| reverse_palette.push("")); (0..schem.palette_max).for_each(|_| reverse_palette.push(""));
for (key, value) in schem.get_palette().iter() { for (key, value) in schem.palette.iter() {
reverse_palette[*value as usize] = key; reverse_palette[*value as usize] = key;
} }
reverse_palette reverse_palette
} }
pub fn strip_data(schem: &SchematicVersioned) -> SchematicVersioned { pub fn strip_data(schem: &SpongeSchematic) -> SpongeSchematic {
let mut data: Vec<i32> = Vec::new(); let mut data: Vec<i32> = Vec::new();
let mut palette: HashMap<String, i32> = HashMap::new(); let mut palette: HashMap<String, i32> = HashMap::new();
let mut palette_max: i32 = 0; let mut palette_max: i32 = 0;
let reverse_palette = create_reverse_palette(schem); let reverse_palette = create_reverse_palette(schem);
for block in schem.get_block_data().iter() { for block in schem.block_data.iter() {
let block_name = reverse_palette[*block as usize].clone(); let block_name = reverse_palette[*block as usize];
let block_name = block_name.split('[').next().unwrap().to_string(); let block_name = block_name.split('[').next().unwrap().to_string();
let entry = palette.entry(block_name).or_insert_with(|| { let entry = palette.entry(block_name).or_insert_with(|| {
@@ -48,28 +48,26 @@ pub fn strip_data(schem: &SchematicVersioned) -> SchematicVersioned {
data.push(*entry); data.push(*entry);
} }
SchematicVersioned::V2(SpongeV2Schematic { SpongeSchematic {
data_version: 1, data_version: 1,
palette, palette,
palette_max, palette_max,
block_data: data, block_data: data,
block_entities: schem.get_block_entities().clone(), block_entities: schem.block_entities.clone(),
height: schem.get_height(), height: schem.height,
length: schem.get_length(), length: schem.length,
width: schem.get_width(), width: schem.width,
metadata: CompoundTag::new(), metadata: CompoundTag::new(),
offset: [0; 3], offset: [0; 3],
entities: None, entities: None,
},) }
} }
pub fn match_palette_adapt(schem: &SchematicVersioned, matching_palette: &HashMap<String, i32>, ignore_data: bool) -> Vec<i32> { pub fn match_palette_adapt(schem: &SpongeSchematic, matching_palette: &HashMap<String, i32>, ignore_data: bool) -> Vec<i32> {
let mut data: Vec<i32> = Vec::new(); let mut data = Vec::with_capacity(schem.block_data.len());
let reverse_palette = create_reverse_palette(schem); let reverse_palette = create_reverse_palette(schem);
for x in schem.get_block_data() { for x in schem.block_data.as_slice().iter() {
let blockname = reverse_palette[*x as usize]; let blockname = reverse_palette[*x as usize];
let blockname = if ignore_data { normalize_data(blockname, ignore_data) } else { blockname }; let blockname = if ignore_data { normalize_data(blockname, ignore_data) } else { blockname };
let block_id = match matching_palette.get(&*blockname) { let block_id = match matching_palette.get(&*blockname) {
@@ -83,10 +81,10 @@ pub fn match_palette_adapt(schem: &SchematicVersioned, matching_palette: &HashMa
} }
pub fn match_palette( pub fn match_palette(
schem: &SchematicVersioned, schem: &SpongeSchematic,
pattern: &SchematicVersioned, pattern: &SpongeSchematic,
ignore_data: bool, ignore_data: bool,
) -> SchematicVersioned { ) -> SpongeSchematic {
if ignore_data { if ignore_data {
match_palette_internal(&strip_data(schem), &strip_data(pattern), ignore_data) match_palette_internal(&strip_data(schem), &strip_data(pattern), ignore_data)
} else { } else {
@@ -95,23 +93,23 @@ pub fn match_palette(
} }
fn match_palette_internal( fn match_palette_internal(
schem: &SchematicVersioned, schem: &SpongeSchematic,
pattern: &SchematicVersioned, pattern: &SpongeSchematic,
ignore_data: bool, ignore_data: bool,
) -> SchematicVersioned { ) -> SpongeSchematic {
let data_pattern: Vec<i32> = match_palette_adapt(&pattern, schem.get_palette(), ignore_data); let data_pattern: Vec<i32> = match_palette_adapt(&pattern, &schem.palette, ignore_data);
SchematicVersioned::V2(SpongeV2Schematic { SpongeSchematic {
data_version: 0, data_version: 0,
palette: schem.get_palette().clone(), palette: schem.palette.clone(),
palette_max: schem.get_palette_max(), palette_max: schem.palette_max,
block_data: data_pattern, block_data: data_pattern,
block_entities: pattern.get_block_entities().clone(), block_entities: pattern.block_entities.clone(),
height: pattern.get_height(), height: pattern.height,
length: pattern.get_length(), length: pattern.length,
width: pattern.get_width(), width: pattern.width,
metadata: CompoundTag::new(), metadata: CompoundTag::new(),
offset: [0; 3], offset: [0; 3],
entities: None, entities: None,
}) }
} }

View File

@@ -7,7 +7,7 @@ license = "AGPL-3.0-or-later"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
sqlx = { version = "0.6", features = [ "runtime-async-std-native-tls" , "mysql" ] } sqlx = { version = "0.7", features = [ "runtime-async-std-native-tls" , "mysql" ] }
schemsearch-lib = { path = "../schemsearch-lib" } schemsearch-lib = { path = "../schemsearch-lib" }
schemsearch-files = { path = "../schemsearch-files" } schemsearch-files = { path = "../schemsearch-files" }

BIN
tests/Pattern.nbt Normal file

Binary file not shown.

BIN
tests/Random.nbt Normal file

Binary file not shown.