use core::net::{IpAddr, Ipv4Addr, SocketAddr};
extern crate alloc;
use alloc::string::{String, ToString};
use alloc::vec::Vec;
use crate::common::site::Site;
use crate::{
Context, OrtResult, Read, Write, chunked,
common::{buf_read, config, resolver},
http,
input::args,
};
const MAX_TOTAL_SLUG_LEN: usize = 16 * 1024;
pub fn run(
api_key: &str,
settings: config::Settings,
opts: args::ListOpts,
site: &'static Site,
mut w: impl Write,
) -> OrtResult<()> {
let addr = if settings.dns.is_empty() {
let ip = unsafe { resolver::resolve(site.dns_label)? };
SocketAddr::new(IpAddr::V4(ip), site.port)
} else {
settings
.dns
.first()
.map(|a| {
let ip_addr = a.parse::<Ipv4Addr>().unwrap();
SocketAddr::new(IpAddr::V4(ip_addr), site.port)
})
.unwrap()
};
let reader = http::list_models(api_key, site.host, site.list_url, alloc::vec![addr])
.context("list_models connect")?;
let mut reader = buf_read::OrtBufReader::new(reader);
let is_chunked = http::skip_header(&mut reader)?;
if opts.is_json {
if is_chunked {
const MAX_CHUNK_SIZE: usize = 128 * 1024;
let mut chunked = chunked::read::<_, MAX_CHUNK_SIZE>(reader);
while let Some(chunk) = chunked.next_chunk() {
let chunk = chunk?;
w.write_all(chunk.as_bytes()).context("write models JSON")?;
}
} else {
let mut buf: [u8; 4096] = [0; 4096];
loop {
let bytes_read = reader.read(&mut buf).context("read models body")?;
if bytes_read == 0 {
break;
}
w.write_all(&buf[..bytes_read])
.context("write models JSON")?;
}
}
w.flush().context("flush models JSON")?;
} else {
let mut slugs = Vec::with_capacity(400);
let mut total_slug_len = 0;
if is_chunked {
let mut partial = String::with_capacity(2048);
const MAX_CHUNK_SIZE: usize = 128 * 1024;
let mut chunked = chunked::read::<_, MAX_CHUNK_SIZE>(reader);
while let Some(chunk) = chunked.next_chunk() {
let chunk = chunk?;
for (pos, section) in chunk.split(r#""id":""#).enumerate() {
let maybe_next_id = if pos == 0 && !partial.is_empty() {
partial.push_str(section);
until_quote(&partial)
} else if pos == 0 {
continue;
} else {
until_quote(section)
};
match maybe_next_id {
Some(slug) => {
let mut slug_line = String::with_capacity(slug.len() + 1);
slug_line.push_str(slug);
slug_line.push('\n');
total_slug_len += slug_line.len();
slugs.push(slug_line);
partial.clear();
}
None => {
partial.push_str(section);
}
}
}
}
} else {
let mut models = String::with_capacity(512 * 1024);
reader
.read(unsafe { models.as_mut_vec().as_mut_slice() })
.context("read models body")?;
for slug in models.split(r#""id":""#).skip(1).filter_map(until_quote) {
let slug_line = slug.to_string() + "\n";
total_slug_len += slug_line.len();
slugs.push(slug_line);
}
};
slugs.sort();
if total_slug_len > MAX_TOTAL_SLUG_LEN {
panic!("Too many models in list. Increase MAX_TOTAL_SLUG_LEN in code.");
}
let mut out: [u8; _] = [0u8; MAX_TOTAL_SLUG_LEN];
let mut ptr_out = out.as_mut_ptr();
for s in slugs {
let b = s.as_bytes();
unsafe {
core::ptr::copy_nonoverlapping(b.as_ptr(), ptr_out, b.len());
ptr_out = ptr_out.add(b.len());
}
}
let out_len = unsafe { ptr_out.offset_from(out.as_ptr()) as usize };
let _ = w.write(&out[..out_len]); }
Ok(())
}
fn until_quote(s: &str) -> Option<&str> {
let mut qp = 0;
let len = s.len();
let b = s.as_bytes();
while qp < len && b[qp] != b'"' {
qp += 1;
}
if qp == len { None } else { Some(&s[..qp]) }
}