Skip to content

Commit 6fa3190

Browse files
committed
Add inline Var and Num construction
- Support reading new license key format - Deprecate request_offline_key - Add get_license_key
1 parent 98a503b commit 6fa3190

File tree

9 files changed

+265
-76
lines changed

9 files changed

+265
-76
lines changed

.github/workflows/coverage.yml

+2
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ jobs:
1717
uses: taiki-e/install-action@nextest
1818
- name: Generate code coverage
1919
run: cargo llvm-cov --workspace --codecov --output-path codecov.json
20+
env:
21+
SYMBOLICA_LICENSE: ${{ secrets.SYMBOLICA_LICENSE }}
2022
- name: Upload coverage to Codecov
2123
uses: codecov/codecov-action@v4
2224
with:

src/api/cpp.rs

+14-8
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use std::ffi::{c_char, CStr, CString};
1+
use std::ffi::{c_char, CStr};
22
use std::fmt::Write;
33
use std::os::raw::c_ulonglong;
44
use std::sync::Arc;
@@ -28,6 +28,7 @@ struct LocalState {
2828

2929
struct Symbolica {
3030
local_state: LocalState,
31+
is_licensed: bool,
3132
}
3233

3334
/// Set the Symbolica license key for this computer. Can only be called before calling any other Symbolica functions.
@@ -79,13 +80,11 @@ unsafe extern "C" fn request_trial_license(
7980
/// Get a license key for offline use, generated from a licensed Symbolica session. The key will remain valid for 24 hours.
8081
/// The key is written into `key`, which must be a buffer of at least 100 bytes.
8182
#[no_mangle]
82-
unsafe extern "C" fn get_offline_license_key(key: *mut c_char) -> bool {
83-
match LicenseManager::get_offline_license_key() {
84-
Ok(k) => {
85-
let cs = CString::new(k).unwrap();
86-
key.copy_from_nonoverlapping(cs.as_ptr(), cs.as_bytes_with_nul().len());
87-
true
88-
}
83+
unsafe extern "C" fn get_license_key(email: *const c_char) -> bool {
84+
let email = unsafe { CStr::from_ptr(email) }.to_str().unwrap();
85+
86+
match LicenseManager::get_license_key(email) {
87+
Ok(()) => true,
8988
Err(e) => {
9089
eprintln!("{}", e);
9190
false
@@ -96,6 +95,8 @@ unsafe extern "C" fn get_offline_license_key(key: *mut c_char) -> bool {
9695
/// Create a new Symbolica handle.
9796
#[no_mangle]
9897
unsafe extern "C" fn init() -> *mut Symbolica {
98+
LicenseManager::check();
99+
99100
let s = Symbolica {
100101
local_state: LocalState {
101102
buffer: String::with_capacity(2048),
@@ -104,6 +105,7 @@ unsafe extern "C" fn init() -> *mut Symbolica {
104105
input_has_rational_numbers: false,
105106
exp_fits_in_u8: false,
106107
},
108+
is_licensed: LicenseManager::is_licensed(),
107109
};
108110

109111
Box::into_raw(Box::new(s))
@@ -154,6 +156,10 @@ unsafe extern "C" fn simplify(
154156

155157
let symbolica = unsafe { &mut *symbolica };
156158

159+
if !symbolica.is_licensed {
160+
LicenseManager::check();
161+
}
162+
157163
let token = Token::parse(cstr).unwrap();
158164

159165
let opts = PrintOptions {

src/api/python.rs

+6-4
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ fn symbolica(_py: Python, m: &PyModule) -> PyResult<()> {
9090
m.add_function(wrap_pyfunction!(request_hobbyist_license, m)?)?;
9191
m.add_function(wrap_pyfunction!(request_trial_license, m)?)?;
9292
m.add_function(wrap_pyfunction!(request_sublicense, m)?)?;
93-
m.add_function(wrap_pyfunction!(get_offline_license_key, m)?)?;
93+
m.add_function(wrap_pyfunction!(get_license_key, m)?)?;
9494

9595
m.add("__version__", env!("CARGO_PKG_VERSION"))?;
9696

@@ -147,10 +147,12 @@ fn request_sublicense(
147147
.map_err(exceptions::PyConnectionError::new_err)
148148
}
149149

150-
/// Get a license key for offline use, generated from a licensed Symbolica session. The key will remain valid for 24 hours.
150+
/// Get the license key for the account registered with the provided email address.
151151
#[pyfunction]
152-
fn get_offline_license_key() -> PyResult<String> {
153-
LicenseManager::get_offline_license_key().map_err(exceptions::PyValueError::new_err)
152+
fn get_license_key(email: String) -> PyResult<()> {
153+
LicenseManager::get_license_key(&email)
154+
.map(|_| println!("A license key was sent to your e-mail address."))
155+
.map_err(exceptions::PyConnectionError::new_err)
154156
}
155157

156158
/// Specifies the type of the atom.

src/atom.rs

+14
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
mod coefficient;
22
pub mod representation;
33

4+
use representation::{InlineNum, InlineVar};
5+
46
use crate::{
57
coefficient::Coefficient,
68
parser::Token,
@@ -203,6 +205,18 @@ impl<'a> AsAtomView<'a> for AtomView<'a> {
203205
}
204206
}
205207

208+
impl<'a> AsAtomView<'a> for &'a InlineVar {
209+
fn as_atom_view(self) -> AtomView<'a> {
210+
self.as_view()
211+
}
212+
}
213+
214+
impl<'a> AsAtomView<'a> for &'a InlineNum {
215+
fn as_atom_view(self) -> AtomView<'a> {
216+
self.as_view()
217+
}
218+
}
219+
206220
impl<'a, T: AsRef<Atom>> AsAtomView<'a> for &'a T {
207221
fn as_atom_view(self) -> AtomView<'a> {
208222
self.as_ref().as_view()

src/atom/representation.rs

+92
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,98 @@ const ZERO_DATA: [u8; 3] = [NUM_ID, 1, 0];
3737

3838
pub type RawAtom = Vec<u8>;
3939

40+
/// An inline variable.
41+
pub struct InlineVar {
42+
data: [u8; 16],
43+
size: u8,
44+
}
45+
46+
impl InlineVar {
47+
/// Create a new inline variable.
48+
pub fn new(symbol: Symbol) -> InlineVar {
49+
let mut data = [0; 16];
50+
let mut flags = VAR_ID;
51+
match symbol.wildcard_level {
52+
0 => {}
53+
1 => flags |= VAR_WILDCARD_LEVEL_1,
54+
2 => flags |= VAR_WILDCARD_LEVEL_2,
55+
_ => flags |= VAR_WILDCARD_LEVEL_3,
56+
}
57+
58+
if symbol.is_symmetric {
59+
flags |= FUN_SYMMETRIC_FLAG;
60+
}
61+
if symbol.is_linear {
62+
flags |= FUN_LINEAR_FLAG;
63+
}
64+
if symbol.is_antisymmetric {
65+
flags |= VAR_ANTISYMMETRIC_FLAG;
66+
}
67+
68+
data[0] = flags;
69+
70+
let size = 1 + (symbol.id as u64, 1).get_packed_size() as u8;
71+
(symbol.id as u64, 1).write_packed_fixed(&mut data[1..]);
72+
InlineVar { data, size }
73+
}
74+
75+
pub fn get_data(&self) -> &[u8] {
76+
&self.data[..self.size as usize]
77+
}
78+
79+
pub fn as_var_view(&self) -> VarView {
80+
VarView {
81+
data: &self.data[..self.size as usize],
82+
}
83+
}
84+
85+
pub fn as_view(&self) -> AtomView {
86+
AtomView::Var(VarView {
87+
data: &self.data[..self.size as usize],
88+
})
89+
}
90+
}
91+
92+
impl From<Symbol> for InlineVar {
93+
fn from(symbol: Symbol) -> InlineVar {
94+
InlineVar::new(symbol)
95+
}
96+
}
97+
98+
/// An inline rational number that has 64-bit components.
99+
pub struct InlineNum {
100+
data: [u8; 24],
101+
size: u8,
102+
}
103+
104+
impl InlineNum {
105+
/// Create a new inline number. The gcd of num and den should be 1.
106+
pub fn new(num: i64, den: i64) -> InlineNum {
107+
let mut data = [0; 24];
108+
data[0] = NUM_ID;
109+
110+
let size = 1 + (num, den).get_packed_size() as u8;
111+
(num, den).write_packed_fixed(&mut data[1..]);
112+
InlineNum { data, size }
113+
}
114+
115+
pub fn get_data(&self) -> &[u8] {
116+
&self.data[..self.size as usize]
117+
}
118+
119+
pub fn as_num_view(&self) -> NumView {
120+
NumView {
121+
data: &self.data[..self.size as usize],
122+
}
123+
}
124+
125+
pub fn as_view(&self) -> AtomView {
126+
AtomView::Num(NumView {
127+
data: &self.data[..self.size as usize],
128+
})
129+
}
130+
}
131+
40132
impl Atom {
41133
/// Read from a binary stream. The format is the byte-length first
42134
/// followed by the data.

0 commit comments

Comments
 (0)