Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Directory::searchRange, Directory::entrySelector and Direcoty::rangeShift computation are incorrect #160

Closed
be5invis opened this issue Apr 21, 2018 · 11 comments

Comments

@be5invis
Copy link

be5invis commented Apr 21, 2018

https://github.com/devongovett/fontkit/blob/master/src/tables/directory.js#L47

Per OTS it should be:

this.searchRange = Math.pow(2, Math.floor(Math.log(this.numTables) / Math.LN2)) * 16;
this.entrySelector = Math.floor(Math.log(this.numTables) / Math.LN2);
this.rangeShift = this.numTables * 16 - this.searchRange;
@be5invis be5invis changed the title Directory::searchRange computation is incorrect Directory::searchRange, Directory::entrySelector and Direcoty::rangeShift computation is incorrect Apr 21, 2018
@be5invis be5invis changed the title Directory::searchRange, Directory::entrySelector and Direcoty::rangeShift computation is incorrect Directory::searchRange, Directory::entrySelector and Direcoty::rangeShift computation are incorrect Apr 21, 2018
@Pomax
Copy link
Contributor

Pomax commented Apr 23, 2018

The exact text in the spec is:

  • searchRange: (Maximum power of 2 <= numTables) x 16.
  • entrySelector: Log2(maximum power of 2 <= numTables).
  • rangeShift: NumTables x 16 - searchRange.

This gives us a recipe for really small and efficient code:

  • _power: the x for which 2**x is the maximum power of 2 <= numTables
  • _maxPowerOf2: 2 ** _power
  • searchRange: _maxPowerOf2 x 16.
  • entrySelector: _power - we already computed this as first operation
  • rangeShift: NumTables x 16 - searchRange.

So we can rearrange this:

  • entrySelector: (the x for which 2**x is the maximum power of 2 <= numTables)
  • searchRange: 2 ** entrySelector x 16.
  • rangeShift: NumTables x 16 - searchRange.

Which translates to the following JS, using bit operations rather than arithmetic:

this.entrySelector = (Math.log(this.numTables)/Math.LN2) | 0;
let maxPowerOf2 = 1 << this.entrySelector;
this.searchRange = maxPowerOf2 << 4;
this.rangeShift = this.numTables << 4 - this.searchRange

(We know that the |0 is a safe operation here, because entrySelector is a uint16, whereas ...|0 is a uint32 operation in JS)

And of course if we want more verbosity:

this.entrySelector = Math.floor((Math.log(this.numTables)/Math.LN2));
let maxPowerOf2 = 2 ** this.entrySelector;
this.searchRange = maxPowerOf2 * 16;
this.rangeShift = this.numTables * 16<< 4 - this.searchRange

@be5invis
Copy link
Author

be5invis commented Apr 23, 2018 via email

@be5invis
Copy link
Author

searchRange: (Maximum power of 2 <= numTables) x 16.
I think it means that “Maximum integer that having the form 2**x where x is an integer.”

@behdad
Copy link

behdad commented Apr 24, 2018

So OTS, FontForge, TTX and OTFCC are all wrong? @behdad

Why?

@be5invis
Copy link
Author

@behdad
I mean that the current implementation in these libs are current. IIf Pomax think his answer is OK then all other libraries are wrong.

@behdad
Copy link

behdad commented Apr 25, 2018

I don't understand. Both look correct to me. Which part am I missing?

@be5invis
Copy link
Author

@behdad So we need some clarification... to figure out which side (Fontkit vs OTS) is correct.
Currently only Fontkit is unique. Other libraries are consistent with OTS.

@behdad
Copy link

behdad commented Apr 25, 2018

What's different about it? I've been staying at this code, and I don't see what you mean. It looks equivalent to OTS and TTX codes.

@be5invis
Copy link
Author

be5invis commented Apr 25, 2018

@behdad
Fontkit's code:

function calc_FK() {
	this.searchRange = Math.floor(Math.log(this.numTables) / Math.LN2) * 16;
	this.entrySelector = Math.floor(this.searchRange / Math.LN2);
	this.rangeShift = this.numTables * 16 - this.searchRange;
	return this;
}

OTS, TTX, FontForge, OTFCC... 's code:

function calc_OTS() {
	this.searchRange = Math.pow(2, Math.floor(Math.log(this.numTables) / Math.LN2)) * 16;
	this.entrySelector = Math.floor(Math.log(this.numTables) / Math.LN2);
	this.rangeShift = this.numTables * 16 - this.searchRange;
	return this;
}

The difference:

calc_FK.call({numTables: 23})
// => {numTables: 23, searchRange: 64, entrySelector: 92, rangeShift: 304}
calc_OTS.call({numTables: 23})
// => {numTables: 23, searchRange: 256, entrySelector: 4, rangeShift: 112}

@behdad
Copy link

behdad commented Apr 25, 2018

IIf Pomax think his answer is OK then all other libraries are wrong.

Pomax's code looks right to me, and consistent with other libraries:

 this.entrySelector = Math.floor((Math.log(this.numTables)/Math.LN2));
 let maxPowerOf2 = 2 ** this.entrySelector;
 this.searchRange = maxPowerOf2 * 16;
 this.rangeShift = this.numTables * 16<< 4 - this.searchRange 

this is not what you show in calc_FK though. Yes, calc_FK is wrong.

@be5invis
Copy link
Author

Closed per #178.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants