1use crate::*;
2use rayon::slice::ParallelSliceMut;
3use std::str::from_utf8_unchecked;
4
5pub fn artist(text: &[u8]) -> &str {
6 debug_assert_eq!(text.len(), TEXT_LEN);
7 unsafe {
8 let end = u16::from_le_bytes(text[0..2].try_into().unwrap_unchecked()) as usize + 2;
9 from_utf8_unchecked(&text[2..end])
10 }
11}
12
13pub fn album(text: &[u8]) -> &str {
14 debug_assert_eq!(text.len(), TEXT_LEN);
15 unsafe {
16 let artist_len = u16::from_le_bytes(text[0..2].try_into().unwrap_unchecked()) as usize;
17 let album_len = u16::from_le_bytes(
18 text[2 + artist_len..2 + artist_len + 2]
19 .try_into()
20 .unwrap_unchecked(),
21 ) as usize;
22 let album = 2 + artist_len + 2..artist_len + 2 + album_len + 2;
23 from_utf8_unchecked(&text[album])
24 }
25}
26
27pub fn title(text: &[u8]) -> &str {
28 debug_assert_eq!(text.len(), TEXT_LEN);
29 unsafe {
30 let artist_len = u16::from_le_bytes(text[0..2].try_into().unwrap_unchecked()) as usize;
31 let album_len = u16::from_le_bytes(
32 text[2 + artist_len..2 + artist_len + 2]
33 .try_into()
34 .unwrap_unchecked(),
35 ) as usize;
36
37 let title_len = u16::from_le_bytes(
38 text[2 + artist_len + 2 + album_len..2 + artist_len + 2 + album_len + 2]
39 .try_into()
40 .unwrap_unchecked(),
41 ) as usize;
42
43 let title =
44 2 + artist_len + 2 + album_len + 2..artist_len + 2 + album_len + 2 + title_len + 2;
45
46 from_utf8_unchecked(&text[title])
47 }
48}
49
50pub fn path(text: &[u8]) -> &str {
51 debug_assert_eq!(text.len(), TEXT_LEN);
52 unsafe {
53 let artist_len = u16::from_le_bytes(text[0..2].try_into().unwrap_unchecked()) as usize;
54 let album_len = u16::from_le_bytes(
55 text[2 + artist_len..2 + artist_len + 2]
56 .try_into()
57 .unwrap_unchecked(),
58 ) as usize;
59
60 let title_len = u16::from_le_bytes(
61 text[2 + artist_len + 2 + album_len..2 + artist_len + 2 + album_len + 2]
62 .try_into()
63 .unwrap_unchecked(),
64 ) as usize;
65
66 let path_len = u16::from_le_bytes(
67 text[2 + artist_len + 2 + album_len + 2 + title_len
68 ..2 + artist_len + 2 + album_len + 2 + title_len + 2]
69 .try_into()
70 .unwrap_unchecked(),
71 ) as usize;
72
73 let path = 2 + artist_len + 2 + album_len + 2 + title_len + 2
74 ..artist_len + 2 + album_len + 2 + title_len + 2 + path_len + 2;
75
76 from_utf8_unchecked(&text[path])
77 }
78}
79
80pub fn artist_and_album(text: &[u8]) -> (&str, &str) {
81 debug_assert_eq!(text.len(), TEXT_LEN);
82 unsafe {
83 let artist_len = u16::from_le_bytes(text[0..2].try_into().unwrap_unchecked()) as usize;
84 let artist = 2..artist_len + 2;
85
86 let album_len = u16::from_le_bytes(
87 text[2 + artist_len..2 + artist_len + 2]
88 .try_into()
89 .unwrap_unchecked(),
90 ) as usize;
91 let album = 2 + artist_len + 2..artist_len + 2 + album_len + 2;
92
93 (
94 from_utf8_unchecked(&text[artist]),
95 from_utf8_unchecked(&text[album]),
96 )
97 }
98}
99
100pub fn get(index: usize) -> Option<Song> {
101 if let Some(mmap) = mmap() {
102 let start = SONG_LEN * index;
103 let bytes = mmap.get(start..start + SONG_LEN)?;
104 Some(Song::from(bytes, index))
105 } else {
106 None
107 }
108}
109
110pub fn ids(ids: &[usize]) -> Vec<Song> {
111 if let Some(mmap) = mmap() {
112 let mut songs = Vec::new();
113 for id in ids {
114 let start = SONG_LEN * id;
115 let bytes = &mmap[start..start + SONG_LEN];
116 songs.push(Song::from(bytes, *id));
117 }
118 songs
120 } else {
121 Vec::new()
122 }
123}
124
125pub fn songs_from_album(ar: &str, al: &str) -> Vec<Song> {
126 if let Some(mmap) = mmap() {
127 let mut songs = Vec::new();
128 let mut i = 0;
129 while let Some(text) = mmap.get(i..i + TEXT_LEN) {
130 let (artist, album) = artist_and_album(text);
131 if artist == ar && album == al {
132 songs.push(Song::from(&mmap[i..i + SONG_LEN], i / SONG_LEN));
133 }
134 i += SONG_LEN;
135 }
136 songs.sort_unstable();
137 songs
138 } else {
139 Vec::new()
140 }
141}
142
143pub fn albums_by_artist(ar: &str) -> Vec<String> {
144 if let Some(mmap) = mmap() {
145 let mut albums = Vec::new();
146 let mut i = 0;
147 while let Some(text) = mmap.get(i..i + TEXT_LEN) {
148 let artist = artist(text);
149 if artist == ar {
150 albums.push(album(text).to_string());
151 }
152 i += SONG_LEN;
153 }
154 albums.sort_unstable_by_key(|album| album.to_ascii_lowercase());
155 albums.dedup();
156 albums
157 } else {
158 Vec::new()
159 }
160}
161
162pub fn songs_by_artist(ar: &str) -> Vec<Song> {
163 if let Some(mmap) = mmap() {
164 let mut songs = Vec::new();
165 let mut i = 0;
166 while let Some(text) = mmap.get(i..i + TEXT_LEN) {
167 let artist = artist(text);
168 if artist == ar {
169 let song_bytes = &mmap[i..i + SONG_LEN];
170 songs.push(Song::from(song_bytes, i / SONG_LEN));
171 }
172 i += SONG_LEN;
173 }
174 songs.sort_unstable();
175 songs
176 } else {
177 Vec::new()
178 }
179}
180
181pub fn artists() -> Vec<String> {
182 if let Some(mmap) = mmap() {
183 let mut artists = Vec::new();
184 let mut i = 0;
185 while let Some(text) = mmap.get(i..i + TEXT_LEN) {
186 artists.push(artist(text).to_string());
187 i += SONG_LEN;
188 }
189 artists.sort_unstable_by_key(|artist| artist.to_ascii_lowercase());
190 artists.dedup();
191 artists
192 } else {
193 Vec::new()
194 }
195}
196
197pub fn artists_albums_and_songs() -> (Vec<String>, Vec<(String, String)>, Vec<Song>) {
198 if let Some(mmap) = mmap() {
199 let songs: Vec<Song> = (0..len())
200 .into_par_iter()
201 .map(|i| {
202 let pos = i * SONG_LEN;
203 let bytes = unsafe { &mmap.get_unchecked(pos..pos + SONG_LEN) };
204 Song::from(bytes, i)
205 })
206 .collect();
207
208 let mut albums: Vec<(&str, &str)> = songs
209 .iter()
210 .map(|song| (song.artist.as_str(), song.album.as_str()))
211 .collect();
212 albums.par_sort_unstable_by_key(|(artist, _album)| artist.to_ascii_lowercase());
213 albums.dedup();
214 let albums: Vec<(String, String)> = albums
215 .into_iter()
216 .map(|(artist, album)| (artist.to_owned(), album.to_owned()))
217 .collect();
218
219 let mut artists: Vec<String> = albums
220 .iter()
221 .map(|(artist, _album)| artist.clone())
222 .collect();
223 artists.dedup();
224
225 (artists, albums, songs)
226 } else {
227 (Vec::new(), Vec::new(), Vec::new())
228 }
229}
230
231pub fn len() -> usize {
232 if let Some(mmap) = mmap() {
233 mmap.len() / SONG_LEN
234 } else {
235 0
236 }
237}