Improved device model detection

This commit is contained in:
Jonas Lochmann 2022-05-09 02:00:00 +02:00
parent c64875f1c7
commit 148af959c2
No known key found for this signature in database
GPG key ID: 8B8C9AEE10FA5B36
14 changed files with 338 additions and 62 deletions

View file

@ -0,0 +1,139 @@
import { deepStrictEqual } from 'assert'
import { get } from 'https'
import { parse } from 'csv-parse/sync'
import { deflateSync } from 'zlib'
import { writeFileSync } from 'fs'
import { dirname, resolve } from 'path'
import { fileURLToPath } from 'url'
const __dirname = dirname(fileURLToPath(import.meta.url))
function pullString(url) {
return new Promise((resolve, reject) => {
get(url, (res) => {
if (res.statusCode !== 200) {
reject(new Error('unexpected status code'))
res.resume()
return
}
const buffers = []
res
.on('data', buffers.push.bind(buffers))
.on('end', () => {
resolve(Buffer.concat(buffers))
})
.on('error', reject)
}).on('error', reject)
})
}
async function main() {
const data =
(await pullString('https://storage.googleapis.com/play_public/supported_devices.csv'))
.toString('utf-16le')
const parsedData = parse(data, {})
deepStrictEqual(parsedData[0], ['Retail Branding', 'Marketing Name', 'Device', 'Model'])
const rows = parsedData.slice(1).map((row) => {
if (row.length !== 4) throw new Error(`got ${row.length} columns: ${row}`)
const [brand, marketingName, device, model] = row
return { brand, marketingName, device, model }
})
const relevantRows = rows.filter((row) => row.marketingName !== row.model)
relevantRows.sort((a, b) => {
if (a.device < b.device) return -1
else if (a.device > b.device) return 1
else if (a.model < b.model) return -1
else if (a.model > b.model) return 1
else return 0
})
const relevantStringsSet = new Set()
relevantRows.forEach((row) => {
relevantStringsSet.add(row.device)
relevantStringsSet.add(row.model)
relevantStringsSet.add(row.marketingName)
})
const relevantStrings = Array.from(relevantStringsSet)
relevantStrings.sort(); relevantStrings.reverse()
const stringToIndex = new Map()
let relevantStringPool = Buffer.alloc(0)
let relevantStringPoolAddCounter = 0
for (const str of relevantStrings) {
if (stringToIndex.has(str)) continue
const oldIndex = relevantStringPool.indexOf(str)
if (oldIndex === -1) {
stringToIndex.set(str, relevantStringPool.length)
relevantStringPool = Buffer.concat([relevantStringPool, Buffer.from(str)])
relevantStringPoolAddCounter++
} else {
stringToIndex.set(str, oldIndex)
}
}
// 4 bytes per string: start index (3 byte) + length (1 byte)
// 3 strings (device, model, marketingName)
const deviceInfoBuffer = Buffer.alloc(relevantRows.length * (3 * 4))
const writeString = (outputIndex, str) => {
const startIndex = stringToIndex.get(str)
if (startIndex === undefined) throw new Error()
if (startIndex > 0xffffff) throw new Error()
deviceInfoBuffer.writeUInt8((startIndex >> 16) & 0xff, outputIndex * 4 + 0)
deviceInfoBuffer.writeUInt8((startIndex >> 8) & 0xff, outputIndex * 4 + 1)
deviceInfoBuffer.writeUInt8(startIndex & 0xff, outputIndex * 4 + 2)
deviceInfoBuffer.writeUInt8(str.length, outputIndex * 4 + 3)
}
relevantRows.forEach((row, index) => {
const outputIndex = index * 3
writeString(outputIndex + 0, row.device)
writeString(outputIndex + 1, row.model)
writeString(outputIndex + 2, row.marketingName)
})
const relevantStringBuffer = Buffer.from(relevantStringPool, 'utf8')
const lengthDataBuffer = Buffer.alloc(8)
lengthDataBuffer.writeUInt32BE(relevantStringBuffer.length, 0)
lengthDataBuffer.writeUInt32BE(relevantRows.length, 4)
const resultData = Buffer.concat([lengthDataBuffer, relevantStringBuffer, deviceInfoBuffer])
const resultDataCompressed = deflateSync(resultData, {})
writeFileSync(resolve(__dirname, '../app/src/main/assets/device-names.bin'), resultDataCompressed)
console.log({
rows: rows.length,
relevantRows: relevantRows.length,
relevantStringCount: relevantStrings.length,
relevantStringPoolAddCounter,
relevantStringPoolLength: relevantStringPool.length,
resultDataLength: resultData.length,
resultDataCompressedLength: resultDataCompressed.length,
compressedBytesPerDevices: resultDataCompressed.length / relevantRows.length
})
}
main().catch((ex) => {
console.warn(ex)
process.exit(1)
})