-
-
Notifications
You must be signed in to change notification settings - Fork 24
Implement GlyphItemIter manually #161
base: master
Are you sure you want to change the base?
Conversation
src/glyph_item_iter.rs
Outdated
} | ||
|
||
impl GlyphItemIter { | ||
pub fn init_end(glyph_item: &mut GlyphItem) -> Option<GlyphItemIter> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I gues this should probably be called new_end
or so, as it's creating a new instance of GlyphItemIter
rather than just initializing it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe, I just don't want problem with docs.
@GuillaumeGomez, @sdroege what do you think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes and the signature of the function should look different
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@sdroege without mut
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This does not need a mutable reference is what I meant :)
} | ||
} | ||
|
||
pub fn prev_cluster(&mut self) -> bool { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe there should also be Iterator
, DoubleEndedIterator
impls for easier usage of this, that have a struct IterItem { start_index: i32, ...}
as item?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IMHO this need be other type instead of this,
with lifetime etc.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why? This literally is an iterator :)
A lifetime parameter is also needed on this type
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because reverse variant (from iter_end
) next()
need call prev_cluster
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
also first call next
need return "current" value instead moving ( for reverse next_back
need return that)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see, so might indeed need an adapter struct :) Weird API!
src/glyph_item_iter.rs
Outdated
iter.to_glib_none_mut().0, | ||
glyph_item.to_glib_none_mut().0, | ||
//Text seems ignored and item's used | ||
"".to_glib_none().0, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is wrong. This must be the text corresponding to the glyph item, or otherwise you get invalid memory accesses. No idea how to enforce that :)
See https://gitlab.gnome.org/GNOME/pango/blob/050ebae3a0340e37187319273b5ab443cbe1f0fd/pango/pango-glyph-item.c#L287-288 for where this is used for example.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Strange but for me this works fine for Layout
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't know, I can only tell you what the code looks like :) It's taking the text pointer without any checks and then adds offsets calculated from the glyph item on it without checking that it's actually in the valid range of the text.
|
||
glib_wrapper! { | ||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] | ||
pub struct GlyphItemIter(Boxed<pango_sys::PangoGlyphItemIter>); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This needs a lifetime parameter. The the glyph item used for initialization must stay alive at least as long as the iterator. The iterator "borrows" the item.
src/glyph_item_iter.rs
Outdated
} | ||
} | ||
|
||
pub fn start_index(&self) -> i32 { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe all these should actually be usize
...
Implement iterator and publish only functions on |
7ee9698
to
21ca84e
Compare
src/glyph_item_iter.rs
Outdated
} | ||
|
||
impl GlyphItemIter { | ||
pub fn init_end(glyph_item: &GlyphItem, text: &str) -> Option<GlyphItemIter> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is still unsafe: you need to ensure that the parameters stay alive as long as the return iter. And you need to ensure that the text matches the glyph item, otherwise invalid memory accesses will happen.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This type not exported, I can change to pub(crate)
to be more explicit,
and only used within lifetimed GlyphItemIterator
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Generally, if soundness of a function depends on its usage (i.e. it doesn't guarantee), it should be marked unsafe
regardless of whether it's internal or not.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh I missed that. Better make it more explicit then, yes :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Generally, if soundness of a function depends on its usage (i.e. it doesn't guarantee), it should be marked unsafe regardless of whether it's internal or not.
Yes, definitely. but the function is also gone now, so... :)
src/glyph_item_iter.rs
Outdated
} | ||
|
||
impl<'a> GlyphItemIterator<'a> { | ||
#[inline(always)] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hm, this function and next unneeded
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I meant the inline annotation. Don't add those unless you benchmarked that it actually makes a positive difference :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated, also removed old unused getters
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would argue that it's a good idea to mark it #[inline]
, because otherwise functions without type parameter like this would not be inlined downstream unless the final binary is built with LTO enabled.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does it even make a difference if it's not inlined?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would have an extra function call whenever downstream invokes it, and if that happens to be in a tight loop, it could be a problem.
My thought is basically that marking it inline wouldn't hurt anything as it's so small. It would just ensure the compiler is able to inline it if it thinks fit.
But maybe applications serious on performance should really enable LTO, so it may not really matter that much.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In theory I agree with you but in practice it's all not that easy and generally a better idea to not do anything unless you actually measure it. The practical effects are not always that obvious. Just like with all optimizations: measure, then fix if needed.
src/glyph_item_iter.rs
Outdated
GlyphItemIterator::new_start(self, text) | ||
} | ||
|
||
pub fn riter<'a>( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there some std library API called riter()
or what names do they use?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought that it exists, but now I not found any, can be any other name then like reverse_iter
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds better to me
src/glyph_item_iter.rs
Outdated
pub end_char: usize, | ||
} | ||
|
||
pub struct GlyphItemIterator<'a> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For extra fun you can make the is_reverse
bool a type parameter for the struct and then get rid of the if
in next()
and let it all be decided at compile-time.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I will try that too
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hm, for this I need write two more iterator impls ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes but each would be half as big. No if/else
Changed constructor visiblity and safety, |
Replaced |
src/glyph_item_iter.rs
Outdated
} | ||
|
||
pub struct GlyphItemIteratorData { | ||
pub start_glyph: i32, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can this be indeed negative?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
https://gitlab.gnome.org/GNOME/pango/blob/050ebae3a0340e37187319273b5ab443cbe1f0fd/pango/pango-glyph-item.c#L481
but on my tests its minimum is 0
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That would be on RTL text, and I think the pango_glyph_item_iter_prev_cluster()
call before returning would then set that to a positive index again. I don't think this can be negative ever.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, I change it too
|
||
pub fn reverse_iter<'a>( | ||
&'a self, | ||
text: &'a str, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How do we ensure that the text corresponds to the glyph item? From reading the code it seems like we otherwise would get invalid memory accesses (if the text is smaller than the glyph)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't know way to ensure :(
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can get PangoItem.length and compare with text length and chars count, but it not give us 100%
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't know either, needs someone to dig into this API
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@EPashkin This one is the remaining issue? I guess this needs some talking to Pango developers to figure out what to do about this here :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IMHO yes
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Any news on this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@GuillaumeGomez Sorry, but I still have no idea how do this safe,
so better just leave it for now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
@EPashkin it works! |
@velyan Only one problem still unsolved #161 (review) 😢 |
I see, sorry I can't help with this either :/ |
Fix #160
@velyan Can you confirm that it works?
Note there also two fields in iterator but its seems not changed on iterating and filled only on initialization.
Also text seems ignored, at minimum for
Layout
its text used anyway,so I removed parameter from constructor.
cc @GuillaumeGomez, @sdroege