diff --git a/CLAUDE.md b/CLAUDE.md index 08106f80..17807614 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -330,6 +330,28 @@ echo 'print("Hello Nyash!")' > local_tests/test_hello.nyash - プラグインのみのビルドは数秒で完了します - Phase 9.75fで動的ライブラリ分離により改善作業中 +### 🔧 **Rustビルドエラー対処法** +**Rustのコンパイルエラーは詳細が見づらいため、以下のパターンで対処:** + +#### 1. エラーをファイルに出力 +```bash +# エラーをファイルに保存して解析 +cargo build --lib -j32 2>&1 > build_errors.txt + +# 特定のエラーコードを検索 +grep -A10 "error\[E0308\]" build_errors.txt +``` + +#### 2. 32スレッドビルドの基本ルール +- **時間制限なし**: `--timeout 300000` (5分)以上を設定 +- **エラー出力**: 必ずファイルに保存して解析 +- **並列度**: `-j32` で最大並列化 + +#### 3. よくあるエラーパターン +- `Box` vs `Arc`: `.into()` で変換 +- `unsafe` ブロックでの型推論: 明示的な型指定が必要 +- deprecatedワーニング: MIR命令の移行期間中は無視可 + ### 🐛 デバッグ #### パーサー無限ループ対策(NEW! 2025-08-09) diff --git a/src/bid/error.rs b/src/bid/error.rs new file mode 100644 index 00000000..f7bf9696 --- /dev/null +++ b/src/bid/error.rs @@ -0,0 +1,80 @@ +/// BID-1 Standard Error Codes +#[repr(i32)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum BidError { + /// Operation successful + Success = 0, + + /// Buffer too small (need to call again with larger buffer) + ShortBuffer = -1, + + /// Invalid type ID + InvalidType = -2, + + /// Invalid method ID + InvalidMethod = -3, + + /// Invalid arguments + InvalidArgs = -4, + + /// Plugin internal error + PluginError = -5, + + /// Memory allocation failed + OutOfMemory = -6, + + /// UTF-8 encoding error + InvalidUtf8 = -7, + + /// Handle not found + InvalidHandle = -8, + + /// Version mismatch + VersionMismatch = -9, +} + +impl BidError { + /// Convert from raw i32 + pub fn from_raw(code: i32) -> Self { + match code { + 0 => BidError::Success, + -1 => BidError::ShortBuffer, + -2 => BidError::InvalidType, + -3 => BidError::InvalidMethod, + -4 => BidError::InvalidArgs, + -5 => BidError::PluginError, + -6 => BidError::OutOfMemory, + -7 => BidError::InvalidUtf8, + -8 => BidError::InvalidHandle, + -9 => BidError::VersionMismatch, + _ => BidError::PluginError, // Unknown errors map to plugin error + } + } + + /// Get human-readable error message + pub fn message(&self) -> &'static str { + match self { + BidError::Success => "Operation successful", + BidError::ShortBuffer => "Buffer too small, call again with larger buffer", + BidError::InvalidType => "Invalid type ID", + BidError::InvalidMethod => "Invalid method ID", + BidError::InvalidArgs => "Invalid arguments", + BidError::PluginError => "Plugin internal error", + BidError::OutOfMemory => "Memory allocation failed", + BidError::InvalidUtf8 => "Invalid UTF-8 encoding", + BidError::InvalidHandle => "Handle not found", + BidError::VersionMismatch => "BID version mismatch", + } + } +} + +impl std::fmt::Display for BidError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{} (code: {})", self.message(), *self as i32) + } +} + +impl std::error::Error for BidError {} + +/// Result type for BID operations +pub type BidResult = Result; \ No newline at end of file diff --git a/src/bid/mod.rs b/src/bid/mod.rs new file mode 100644 index 00000000..96e1e40b --- /dev/null +++ b/src/bid/mod.rs @@ -0,0 +1,23 @@ +// BID-FFI: Box Interface Definition with Foreign Function Interface +// Everything is Box philosophy meets practical FFI/ABI design! + +pub mod types; +pub mod tlv; +pub mod error; + +pub use types::*; +pub use tlv::*; +pub use error::*; + +/// BID-1 version constant +pub const BID_VERSION: u16 = 1; + +/// Platform-dependent pointer size +#[cfg(target_pointer_width = "32")] +pub type Usize = u32; + +#[cfg(target_pointer_width = "64")] +pub type Usize = u64; + +/// Standard alignment requirement +pub const BID_ALIGNMENT: usize = 8; \ No newline at end of file diff --git a/src/bid/tlv.rs b/src/bid/tlv.rs new file mode 100644 index 00000000..9b5da2dd --- /dev/null +++ b/src/bid/tlv.rs @@ -0,0 +1,324 @@ +use super::{BidError, BidResult, BidHandle, BidTag, BID_VERSION}; +use std::mem; + +/// BID-1 TLV Header +#[repr(C)] +#[derive(Debug, Clone, Copy)] +pub struct BidTlvHeader { + pub version: u16, // BID version (1) + pub argc: u16, // Argument count +} + +/// TLV Entry structure +#[repr(C)] +#[derive(Debug, Clone, Copy)] +pub struct TlvEntry { + pub tag: u8, // Type tag + pub reserved: u8, // Reserved for future use (0) + pub size: u16, // Payload size + // Payload follows immediately after +} + +/// TLV encoder for BID-1 format +pub struct TlvEncoder { + buffer: Vec, + entry_count: u16, +} + +impl TlvEncoder { + /// Create a new TLV encoder + pub fn new() -> Self { + let mut encoder = Self { + buffer: Vec::with_capacity(256), + entry_count: 0, + }; + + // Reserve space for header + encoder.buffer.extend_from_slice(&[0; mem::size_of::()]); + encoder + } + + /// Encode a boolean value + pub fn encode_bool(&mut self, value: bool) -> BidResult<()> { + self.encode_entry(BidTag::Bool, &[if value { 1 } else { 0 }]) + } + + /// Encode a 32-bit integer + pub fn encode_i32(&mut self, value: i32) -> BidResult<()> { + self.encode_entry(BidTag::I32, &value.to_le_bytes()) + } + + /// Encode a 64-bit integer + pub fn encode_i64(&mut self, value: i64) -> BidResult<()> { + self.encode_entry(BidTag::I64, &value.to_le_bytes()) + } + + /// Encode a 32-bit float + pub fn encode_f32(&mut self, value: f32) -> BidResult<()> { + self.encode_entry(BidTag::F32, &value.to_le_bytes()) + } + + /// Encode a 64-bit float + pub fn encode_f64(&mut self, value: f64) -> BidResult<()> { + self.encode_entry(BidTag::F64, &value.to_le_bytes()) + } + + /// Encode a string + pub fn encode_string(&mut self, value: &str) -> BidResult<()> { + let bytes = value.as_bytes(); + if bytes.len() > u16::MAX as usize { + return Err(BidError::InvalidArgs); + } + self.encode_entry(BidTag::String, bytes) + } + + /// Encode binary data + pub fn encode_bytes(&mut self, value: &[u8]) -> BidResult<()> { + if value.len() > u16::MAX as usize { + return Err(BidError::InvalidArgs); + } + self.encode_entry(BidTag::Bytes, value) + } + + /// Encode a handle + pub fn encode_handle(&mut self, handle: BidHandle) -> BidResult<()> { + self.encode_entry(BidTag::Handle, &handle.to_u64().to_le_bytes()) + } + + /// Encode void (no payload) + pub fn encode_void(&mut self) -> BidResult<()> { + self.encode_entry(BidTag::Void, &[]) + } + + /// Internal: encode a TLV entry + fn encode_entry(&mut self, tag: BidTag, payload: &[u8]) -> BidResult<()> { + let entry = TlvEntry { + tag: tag as u8, + reserved: 0, + size: payload.len() as u16, + }; + + // Write entry header + self.buffer.push(entry.tag); + self.buffer.push(entry.reserved); + self.buffer.extend_from_slice(&entry.size.to_le_bytes()); + + // Write payload + self.buffer.extend_from_slice(payload); + + self.entry_count += 1; + Ok(()) + } + + /// Finalize the encoding and return the buffer + pub fn finish(mut self) -> Vec { + // Update header + let header = BidTlvHeader { + version: BID_VERSION, + argc: self.entry_count, + }; + + // Write header at the beginning + self.buffer[0..2].copy_from_slice(&header.version.to_le_bytes()); + self.buffer[2..4].copy_from_slice(&header.argc.to_le_bytes()); + self.buffer + } +} + +/// TLV decoder for BID-1 format +pub struct TlvDecoder<'a> { + data: &'a [u8], + position: usize, + header: BidTlvHeader, +} + +impl<'a> TlvDecoder<'a> { + /// Create a new TLV decoder + pub fn new(data: &'a [u8]) -> BidResult { + if data.len() < mem::size_of::() { + return Err(BidError::InvalidArgs); + } + + // Read header safely + let version = u16::from_le_bytes([data[0], data[1]]); + let argc = u16::from_le_bytes([data[2], data[3]]); + let header = BidTlvHeader { version, argc }; + + if header.version != BID_VERSION { + return Err(BidError::VersionMismatch); + } + + Ok(Self { + data, + position: mem::size_of::(), + header, + }) + } + + /// Get the argument count + pub fn arg_count(&self) -> u16 { + self.header.argc + } + + /// Decode the next entry + pub fn decode_next(&mut self) -> BidResult> { + if self.position >= self.data.len() { + return Ok(None); + } + + // Read entry header + if self.position + mem::size_of::() > self.data.len() { + return Err(BidError::InvalidArgs); + } + + // Read entry safely + let tag = self.data[self.position]; + let reserved = self.data[self.position + 1]; + let size = u16::from_le_bytes([ + self.data[self.position + 2], + self.data[self.position + 3], + ]); + let entry = TlvEntry { tag, reserved, size }; + self.position += mem::size_of::(); + + // Read payload + let payload_end = self.position + entry.size as usize; + if payload_end > self.data.len() { + return Err(BidError::InvalidArgs); + } + + let payload = &self.data[self.position..payload_end]; + self.position = payload_end; + + // Convert tag + let tag = match entry.tag { + 1 => BidTag::Bool, + 2 => BidTag::I32, + 3 => BidTag::I64, + 4 => BidTag::F32, + 5 => BidTag::F64, + 6 => BidTag::String, + 7 => BidTag::Bytes, + 8 => BidTag::Handle, + 9 => BidTag::Void, + 20 => BidTag::Result, + 21 => BidTag::Option, + 22 => BidTag::Array, + _ => return Err(BidError::InvalidType), + }; + + Ok(Some((tag, payload))) + } + + /// Decode a boolean from payload + pub fn decode_bool(payload: &[u8]) -> BidResult { + if payload.len() != 1 { + return Err(BidError::InvalidArgs); + } + Ok(payload[0] != 0) + } + + /// Decode an i32 from payload + pub fn decode_i32(payload: &[u8]) -> BidResult { + if payload.len() != 4 { + return Err(BidError::InvalidArgs); + } + Ok(i32::from_le_bytes([payload[0], payload[1], payload[2], payload[3]])) + } + + /// Decode an i64 from payload + pub fn decode_i64(payload: &[u8]) -> BidResult { + if payload.len() != 8 { + return Err(BidError::InvalidArgs); + } + let mut bytes = [0u8; 8]; + bytes.copy_from_slice(payload); + Ok(i64::from_le_bytes(bytes)) + } + + /// Decode a handle from payload + pub fn decode_handle(payload: &[u8]) -> BidResult { + if payload.len() != 8 { + return Err(BidError::InvalidArgs); + } + let mut bytes = [0u8; 8]; + bytes.copy_from_slice(payload); + Ok(BidHandle::from_u64(u64::from_le_bytes(bytes))) + } + + /// Decode an f32 from payload + pub fn decode_f32(payload: &[u8]) -> BidResult { + if payload.len() != 4 { + return Err(BidError::InvalidArgs); + } + Ok(f32::from_le_bytes([payload[0], payload[1], payload[2], payload[3]])) + } + + /// Decode an f64 from payload + pub fn decode_f64(payload: &[u8]) -> BidResult { + if payload.len() != 8 { + return Err(BidError::InvalidArgs); + } + let mut bytes = [0u8; 8]; + bytes.copy_from_slice(payload); + Ok(f64::from_le_bytes(bytes)) + } + + /// Decode a string from payload + pub fn decode_string(payload: &[u8]) -> BidResult<&str> { + std::str::from_utf8(payload).map_err(|_| BidError::InvalidUtf8) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_encode_decode_primitives() { + let mut encoder = TlvEncoder::new(); + encoder.encode_bool(true).unwrap(); + encoder.encode_i32(42).unwrap(); + encoder.encode_i64(9876543210).unwrap(); + encoder.encode_string("Hello Nyash!").unwrap(); + + let data = encoder.finish(); + let mut decoder = TlvDecoder::new(&data).unwrap(); + + assert_eq!(decoder.arg_count(), 4); + + // Decode bool + let (tag, payload) = decoder.decode_next().unwrap().unwrap(); + assert_eq!(tag, BidTag::Bool); + assert_eq!(TlvDecoder::decode_bool(payload).unwrap(), true); + + // Decode i32 + let (tag, payload) = decoder.decode_next().unwrap().unwrap(); + assert_eq!(tag, BidTag::I32); + assert_eq!(TlvDecoder::decode_i32(payload).unwrap(), 42); + + // Decode i64 + let (tag, payload) = decoder.decode_next().unwrap().unwrap(); + assert_eq!(tag, BidTag::I64); + assert_eq!(TlvDecoder::decode_i64(payload).unwrap(), 9876543210); + + // Decode string + let (tag, payload) = decoder.decode_next().unwrap().unwrap(); + assert_eq!(tag, BidTag::String); + assert_eq!(TlvDecoder::decode_string(payload).unwrap(), "Hello Nyash!"); + } + + #[test] + fn test_encode_decode_handle() { + let mut encoder = TlvEncoder::new(); + let handle = BidHandle::new(6, 12345); + encoder.encode_handle(handle).unwrap(); + + let data = encoder.finish(); + let mut decoder = TlvDecoder::new(&data).unwrap(); + + let (tag, payload) = decoder.decode_next().unwrap().unwrap(); + assert_eq!(tag, BidTag::Handle); + assert_eq!(TlvDecoder::decode_handle(payload).unwrap(), handle); + } +} \ No newline at end of file diff --git a/src/bid/types.rs b/src/bid/types.rs new file mode 100644 index 00000000..342d82e9 --- /dev/null +++ b/src/bid/types.rs @@ -0,0 +1,152 @@ +use super::Usize; + +/// BID-1 Type System (ChatGPT Enhanced Edition) +#[derive(Clone, Debug, PartialEq)] +pub enum BidType { + // === Primitives (pass by value across FFI) === + Bool, // i32 (0=false, 1=true) + I32, // 32-bit signed integer + I64, // 64-bit signed integer + F32, // 32-bit floating point + F64, // 64-bit floating point + + // === Composite types (pass as ptr+len) === + String, // UTF-8 string (ptr: usize, len: usize) + Bytes, // Binary data (ptr: usize, len: usize) + + // === Handle design (ChatGPT recommendation) === + Handle { + type_id: u32, // Box type ID (1=StringBox, 6=FileBox, etc.) + instance_id: u32, // Instance identifier + }, + + // === Meta types === + Void, // No return value + + // === Phase 2 reserved (TLV tags reserved) === + #[allow(dead_code)] + Option(Box), // TLV tag=21 + #[allow(dead_code)] + Result(Box, Box), // TLV tag=20 + #[allow(dead_code)] + Array(Box), // TLV tag=22 +} + +/// Handle representation for efficient Box references +#[repr(C)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +pub struct BidHandle { + pub type_id: u32, + pub instance_id: u32, +} + +impl BidHandle { + /// Create a new handle + pub fn new(type_id: u32, instance_id: u32) -> Self { + Self { type_id, instance_id } + } + + /// Pack into single u64 (type_id << 32 | instance_id) + pub fn to_u64(&self) -> u64 { + ((self.type_id as u64) << 32) | (self.instance_id as u64) + } + + /// Unpack from single u64 + pub fn from_u64(packed: u64) -> Self { + Self { + type_id: (packed >> 32) as u32, + instance_id: (packed & 0xFFFFFFFF) as u32, + } + } +} + +/// TLV (Type-Length-Value) tags for BID-1 +#[repr(u8)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum BidTag { + Bool = 1, // payload: 1 byte (0/1) + I32 = 2, // payload: 4 bytes (little-endian) + I64 = 3, // payload: 8 bytes (little-endian) + F32 = 4, // payload: 4 bytes (IEEE 754) + F64 = 5, // payload: 8 bytes (IEEE 754) + String = 6, // payload: UTF-8 bytes + Bytes = 7, // payload: binary data + Handle = 8, // payload: 8 bytes (type_id + instance_id) + Void = 9, // payload: 0 bytes + + // Phase 2 reserved + Result = 20, + Option = 21, + Array = 22, +} + +impl BidType { + /// Get the TLV tag for this type + pub fn tag(&self) -> BidTag { + match self { + BidType::Bool => BidTag::Bool, + BidType::I32 => BidTag::I32, + BidType::I64 => BidTag::I64, + BidType::F32 => BidTag::F32, + BidType::F64 => BidTag::F64, + BidType::String => BidTag::String, + BidType::Bytes => BidTag::Bytes, + BidType::Handle { .. } => BidTag::Handle, + BidType::Void => BidTag::Void, + _ => panic!("Phase 2 types not yet implemented"), + } + } + + /// Get the expected payload size (None for variable-length types) + pub fn payload_size(&self) -> Option { + match self { + BidType::Bool => Some(1), + BidType::I32 => Some(4), + BidType::I64 => Some(8), + BidType::F32 => Some(4), + BidType::F64 => Some(8), + BidType::Handle { .. } => Some(8), + BidType::Void => Some(0), + BidType::String | BidType::Bytes => None, // Variable length + _ => panic!("Phase 2 types not yet implemented"), + } + } +} + +/// Box type IDs (matching existing Nyash boxes) +#[repr(u32)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum BoxTypeId { + StringBox = 1, + IntegerBox = 2, + BoolBox = 3, + FloatBox = 4, + ArrayBox = 5, + FileBox = 6, // Plugin example + FutureBox = 7, // Existing async support + P2PBox = 8, // Existing P2P support + // ... more box types +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_handle_packing() { + let handle = BidHandle::new(6, 12345); + let packed = handle.to_u64(); + let unpacked = BidHandle::from_u64(packed); + + assert_eq!(handle, unpacked); + assert_eq!(unpacked.type_id, 6); + assert_eq!(unpacked.instance_id, 12345); + } + + #[test] + fn test_type_tags() { + assert_eq!(BidType::Bool.tag(), BidTag::Bool); + assert_eq!(BidType::String.tag(), BidTag::String); + assert_eq!(BidType::Handle { type_id: 6, instance_id: 0 }.tag(), BidTag::Handle); + } +} \ No newline at end of file diff --git a/src/instance.rs b/src/instance.rs index 6197dfc9..56aee71e 100644 --- a/src/instance.rs +++ b/src/instance.rs @@ -516,7 +516,7 @@ mod tests { // フィールドに値を設定 let int_value = Box::new(IntegerBox::new(42)) as Box; - instance.set_field("value", int_value).unwrap(); + instance.set_field("value", int_value.into()).unwrap(); // フィールドの値を取得 let retrieved = instance.get_field("value").unwrap(); diff --git a/src/lib.rs b/src/lib.rs index 91610e1f..e217df4f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -29,6 +29,9 @@ pub mod box_arithmetic; // 🚀 Arithmetic operations moved from box_trait.rs // 🔥 NyashValue Revolutionary System (NEW!) pub mod value; +// 🚀 BID-FFI: Box Interface Definition with FFI (NEW!) +pub mod bid; + // 🌐 P2P Communication Infrastructure (NEW!) pub mod messaging; pub mod transport;